Question

When broadcasting BinaryString through a TChan, what gets copied the whole Binary or just the reference ?


if the whole binary gets copied how to send only the reference ?

Was it helpful?

Solution

Only a reference is written to the TChan, the payload is not copied. It would be far too inefficient to copy all the data all the time, and since the data is immutable (in general, you can cheat), it is safe to only transfer references.

OTHER TIPS

To be a bit more precise than Daniel (and confirm Daniels suspicion in his comment): A pointer to the constructor of the BinaryString (do you mean ByteString?) is written to the TVar.

Let us confirm by checking the relevant code. TChan is built upon TVar, and uses writeTVar to write the value, whch is implemented in GHC.Conc.Sync (and re-exported by GHC.Cont and Control.Concurrent.STM.TVar):

-- |Write the supplied value into a TVar
writeTVar :: TVar a -> a -> STM ()
writeTVar (TVar tvar#) val = STM $ \s1# ->
    case writeTVar# tvar# val s1# of
         s2# -> (# s2#, () #)

The argument is just passed along to the function writeTVar#, which is a primitive operation which is implemented in rts/PrimOps.cmm:

stg_writeTVarzh
{
  W_ trec;
  W_ tvar;
  W_ new_value;

  /* Args: R1 = TVar closure */
  /*       R2 = New value    */

  MAYBE_GC (R1_PTR & R2_PTR, stg_writeTVarzh); // Call to stmWriteTVar may allocate
  trec = StgTSO_trec(CurrentTSO);
  tvar = R1;
  new_value = R2;
  foreign "C" stmWriteTVar(MyCapability() "ptr", trec "ptr", tvar "ptr", new_value "ptr") [];

  jump %ENTRY_CODE(Sp(0));
}

This wrap the following code in rts/STM.c:

void stmWriteTVar(Capability *cap,
                  StgTRecHeader *trec,
                  StgTVar *tvar, 
                  StgClosure *new_value) {

  StgTRecHeader *entry_in = NULL;
  TRecEntry *entry = NULL;
  TRACE("%p : stmWriteTVar(%p, %p)", trec, tvar, new_value);
  ASSERT (trec != NO_TREC);
  ASSERT (trec -> state == TREC_ACTIVE || 
          trec -> state == TREC_CONDEMNED);

  entry = get_entry_for(trec, tvar, &entry_in);

  if (entry != NULL) {
    if (entry_in == trec) {
      // Entry found in our trec
      entry -> new_value = new_value;
    } else {
      // Entry found in another trec
      TRecEntry *new_entry = get_new_entry(cap, trec);
      new_entry -> tvar = tvar;
      new_entry -> expected_value = entry -> expected_value;
      new_entry -> new_value = new_value;
    } 
  } else {
    // No entry found
    StgClosure *current_value = read_current_value(trec, tvar);
    TRecEntry *new_entry = get_new_entry(cap, trec);
    new_entry -> tvar = tvar;
    new_entry -> expected_value = current_value;
    new_entry -> new_value = new_value;
  }

  TRACE("%p : stmWriteTVar done", trec);
}

And here we see that new_value is a pointer that is never looked at, and stored as such.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top