[project @ 2001-11-08 12:46:31 by simonmar]
[ghc-hetmet.git] / ghc / rts / PrimOps.hc
index 711f538..d36c18e 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: PrimOps.hc,v 1.71 2001/02/11 17:51:07 simonmar Exp $
+ * $Id: PrimOps.hc,v 1.84 2001/11/08 12:46:31 simonmar Exp $
  *
  * (c) The GHC Team, 1998-2000
  *
@@ -7,6 +7,7 @@
  *
  * ---------------------------------------------------------------------------*/
 
+#include "Stg.h"
 #include "Rts.h"
 
 #include "RtsFlags.h"
@@ -17,7 +18,6 @@
 #include "Storage.h"
 #include "BlockAlloc.h" /* tmp */
 #include "StablePriv.h"
-#include "HeapStackCheck.h"
 #include "StgRun.h"
 #include "Itimer.h"
 #include "Prelude.h"
@@ -262,6 +262,24 @@ FN_(newByteArrayzh_fast)                           \
    FE_                                                 \
  }
 
+FN_(newPinnedByteArrayzh_fast)                                 \
+ {                                                             \
+   W_ size, stuff_size, n;                                     \
+   StgArrWords* p;                                             \
+   FB_                                                         \
+     MAYBE_GC(NO_PTRS,newPinnedByteArrayzh_fast);              \
+     n = R1.w;                                                 \
+     stuff_size = BYTES_TO_STGWORDS(n);                                \
+     size = sizeofW(StgArrWords)+ stuff_size;                  \
+     p = (StgArrWords *)RET_STGCALL1(P_,allocatePinned,size);  \
+     TICK_ALLOC_PRIM(sizeofW(StgArrWords),stuff_size,0);       \
+     SET_HDR(p, &stg_ARR_WORDS_info, CCCS);                    \
+     p->words = stuff_size;                                    \
+     TICK_RET_UNBOXED_TUP(1)                                   \
+     RET_P(p);                                                 \
+   FE_                                                         \
+ }
+
 FN_(newArrayzh_fast)
 {
   W_ size, n, init;
@@ -314,7 +332,6 @@ FN_(newMutVarzh_fast)
 
    -------------------------------------------------------------------------- */
 
-#ifndef PAR
 FN_(mkForeignObjzh_fast)
 {
   /* R1.p = ptr to foreign object,
@@ -336,7 +353,6 @@ FN_(mkForeignObjzh_fast)
   RET_P(result);
   FE_
 }
-#endif
 
 /* These two are out-of-line for the benefit of the NCG */
 FN_(unsafeThawArrayzh_fast)
@@ -354,8 +370,6 @@ FN_(unsafeThawArrayzh_fast)
    Weak Pointer Primitives
    -------------------------------------------------------------------------- */
 
-#ifndef PAR
-
 FN_(mkWeakzh_fast)
 {
   /* R1.p = key
@@ -419,8 +433,6 @@ FN_(finalizzeWeakzh_fast)
   FE_
 }
 
-#endif /* !PAR */
-
 /* -----------------------------------------------------------------------------
    Arbitrary-precision Integer operations.
    -------------------------------------------------------------------------- */
@@ -530,12 +542,12 @@ FN_(int64ToIntegerzh_fast)
    if ( val < 0LL ) {
      neg = 1;
      val = -val;
-   } 
+   }
 
    hi = (W_)((LW_)val / 0x100000000ULL);
 
    if ( words_needed == 2 )  { 
-      s = 2; 
+      s = 2;
       Hp[-1] = (W_)val;
       Hp[0] = hi;
    } else if ( val != 0 ) {
@@ -600,7 +612,7 @@ FN_(word64ToIntegerzh_fast)
 }
 
 
-#endif /* HAVE_LONG_LONG */
+#endif /* SUPPORT_LONG_LONGS */
 
 /* ToDo: this is shockingly inefficient */
 
@@ -813,6 +825,38 @@ FN_(yieldzh_fast)
   FE_
 }
 
+/* -----------------------------------------------------------------------------
+ * MVar primitives
+ *
+ * take & putMVar work as follows.  Firstly, an important invariant:
+ *
+ *    If the MVar is full, then the blocking queue contains only
+ *    threads blocked on putMVar, and if the MVar is empty then the
+ *    blocking queue contains only threads blocked on takeMVar.
+ *
+ * takeMvar:
+ *    MVar empty : then add ourselves to the blocking queue
+ *    MVar full  : remove the value from the MVar, and
+ *                 blocking queue empty     : return
+ *                 blocking queue non-empty : perform the first blocked putMVar
+ *                                            from the queue, and wake up the
+ *                                            thread (MVar is now full again)
+ *
+ * putMVar is just the dual of the above algorithm.
+ *
+ * How do we "perform a putMVar"?  Well, we have to fiddle around with
+ * the stack of the thread waiting to do the putMVar.  See
+ * stg_block_putmvar and stg_block_takemvar in HeapStackCheck.c for
+ * the stack layout, and the PerformPut and PerformTake macros below.
+ *
+ * It is important that a blocked take or put is woken up with the
+ * take/put already performed, because otherwise there would be a
+ * small window of vulnerability where the thread could receive an
+ * exception and never perform its take or put, and we'd end up with a
+ * deadlock.
+ *
+ * -------------------------------------------------------------------------- */
+
 FN_(newMVarzh_fast)
 {
   StgMVar *mvar;
@@ -835,6 +879,18 @@ FN_(newMVarzh_fast)
   FE_
 }
 
+#define PerformTake(tso, value) ({                     \
+    (tso)->sp[1] = (W_)value;                          \
+    (tso)->sp[0] = (W_)&stg_gc_unpt_r1_ret_info;       \
+  })
+
+#define PerformPut(tso) ({                             \
+    StgClosure *val = (StgClosure *)(tso)->sp[2];      \
+    (tso)->sp[2] = (W_)&stg_gc_noregs_ret_info;                \
+    (tso)->sp += 2;                                    \
+    val;                                               \
+  })
+
 FN_(takeMVarzh_fast)
 {
   StgMVar *mvar;
@@ -870,19 +926,47 @@ FN_(takeMVarzh_fast)
     /* unlock the MVar */
     mvar->header.info = &stg_EMPTY_MVAR_info;
 #endif
-    BLOCK(R1_PTR, takeMVarzh_fast);
+    JMP_(stg_block_takemvar);
   }
 
+  /* we got the value... */
   val = mvar->value;
-  mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
 
-  /* do this last... we might have locked the MVar in the SMP case,
-   * and writing the info pointer will unlock it.
-   */
-  SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+  if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+      /* There are putMVar(s) waiting... 
+       * wake up the first thread on the queue
+       */
+      ASSERT(mvar->head->why_blocked == BlockedOnMVar);
 
-  TICK_RET_UNBOXED_TUP(1);
-  RET_P(val);
+      /* actually perform the putMVar for the thread that we just woke up */
+      mvar->value = PerformPut(mvar->head);
+
+#if defined(GRAN) || defined(PAR)
+      /* ToDo: check 2nd arg (mvar) is right */
+      mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+#else
+      mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
+#endif
+      if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+         mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+      }
+#ifdef SMP
+      /* unlock in the SMP case */
+      SET_INFO(mvar,&stg_FULL_MVAR_info);
+#endif
+      TICK_RET_UNBOXED_TUP(1);
+      RET_P(val);
+  } else {
+      /* No further putMVars, MVar is now empty */
+
+      /* do this last... we might have locked the MVar in the SMP case,
+       * and writing the info pointer will unlock it.
+       */
+      SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+      mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
+      TICK_RET_UNBOXED_TUP(1);
+      RET_P(val);
+  }
   FE_
 }
 
@@ -906,24 +990,54 @@ FN_(tryTakeMVarzh_fast)
   if (info == &stg_EMPTY_MVAR_info) {
 
 #ifdef SMP
-    /* unlock the MVar */
-    mvar->header.info = &stg_EMPTY_MVAR_info;
+      /* unlock the MVar */
+      SET_INFO(mvar,&stg_EMPTY_MVAR_info);
 #endif
 
-    /* HACK: we need a pointer to pass back, so we abuse NO_FINALIZER_closure */
-    RET_NP(0, &stg_NO_FINALIZER_closure);
+      /* HACK: we need a pointer to pass back, 
+       * so we abuse NO_FINALIZER_closure
+       */
+      RET_NP(0, &stg_NO_FINALIZER_closure);
   }
 
+  /* we got the value... */
   val = mvar->value;
-  mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
 
-  /* do this last... we might have locked the MVar in the SMP case,
-   * and writing the info pointer will unlock it.
-   */
-  SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+  if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+      /* There are putMVar(s) waiting... 
+       * wake up the first thread on the queue
+       */
+      ASSERT(mvar->head->why_blocked == BlockedOnMVar);
 
-  TICK_RET_UNBOXED_TUP(1);
-  RET_NP(1,val);
+      /* actually perform the putMVar for the thread that we just woke up */
+      mvar->value = PerformPut(mvar->head);
+
+#if defined(GRAN) || defined(PAR)
+      /* ToDo: check 2nd arg (mvar) is right */
+      mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+#else
+      mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
+#endif
+      if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+         mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+      }
+#ifdef SMP
+      /* unlock in the SMP case */
+      SET_INFO(mvar,&stg_FULL_MVAR_info);
+#endif
+      TICK_RET_UNBOXED_TUP(1);
+      RET_P(val);
+  } else {
+      /* No further putMVars, MVar is now empty */
+
+      /* do this last... we might have locked the MVar in the SMP case,
+       * and writing the info pointer will unlock it.
+       */
+      SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+      mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
+      TICK_RET_UNBOXED_TUP(1);
+      RET_P(val);
+  }
   FE_
 }
 
@@ -944,35 +1058,114 @@ FN_(putMVarzh_fast)
 #endif
 
   if (info == &stg_FULL_MVAR_info) {
-    R1.cl = (StgClosure *)PutFullMVar_closure;
-    JMP_(raisezh_fast);
+    if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+      mvar->head = CurrentTSO;
+    } else {
+      mvar->tail->link = CurrentTSO;
+    }
+    CurrentTSO->link = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+    CurrentTSO->why_blocked = BlockedOnMVar;
+    CurrentTSO->block_info.closure = (StgClosure *)mvar;
+    mvar->tail = CurrentTSO;
+
+#ifdef SMP
+    /* unlock the MVar */
+    SET_INFO(mvar,&stg_FULL_MVAR_info);
+#endif
+    JMP_(stg_block_putmvar);
   }
   
-  mvar->value = R2.cl;
-
-  /* wake up the first thread on the queue, it will continue with the
-   * takeMVar operation and mark the MVar empty again.
-   */
   if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
-    ASSERT(mvar->head->why_blocked == BlockedOnMVar);
-#if defined(GRAN)
-    mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
-#elif defined(PAR)
-    // ToDo: check 2nd arg (mvar) is right
-    mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+      /* There are takeMVar(s) waiting: wake up the first one
+       */
+      ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+
+      /* actually perform the takeMVar */
+      PerformTake(mvar->head, R2.cl);
+      
+#if defined(GRAN) || defined(PAR)
+      /* ToDo: check 2nd arg (mvar) is right */
+      mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
 #else
-    mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
+      mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
 #endif
-    if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
-      mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
-    }
+      if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+         mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+      }
+#ifdef SMP
+      /* unlocks the MVar in the SMP case */
+      SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+#endif
+      JMP_(ENTRY_CODE(Sp[0]));
+  } else {
+      /* No further takes, the MVar is now full. */
+      mvar->value = R2.cl;
+      /* unlocks the MVar in the SMP case */
+      SET_INFO(mvar,&stg_FULL_MVAR_info);
+      JMP_(ENTRY_CODE(Sp[0]));
   }
 
-  /* unlocks the MVar in the SMP case */
-  SET_INFO(mvar,&stg_FULL_MVAR_info);
+  /* ToDo: yield afterward for better communication performance? */
+  FE_
+}
 
-  /* ToDo: yield here for better communication performance? */
-  JMP_(ENTRY_CODE(Sp[0]));
+FN_(tryPutMVarzh_fast)
+{
+  StgMVar *mvar;
+  const StgInfoTable *info;
+
+  FB_
+  /* args: R1 = MVar, R2 = value */
+
+  mvar = (StgMVar *)R1.p;
+
+#ifdef SMP
+  info = LOCK_CLOSURE(mvar);
+#else
+  info = GET_INFO(mvar);
+#endif
+
+  if (info == &stg_FULL_MVAR_info) {
+
+#ifdef SMP
+    /* unlock the MVar */
+    mvar->header.info = &stg_FULL_MVAR_info;
+#endif
+
+    RET_N(0);
+  }
+  
+  if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+      /* There are takeMVar(s) waiting: wake up the first one
+       */
+      ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+
+      /* actually perform the takeMVar */
+      PerformTake(mvar->head, R2.cl);
+      
+#if defined(GRAN) || defined(PAR)
+      /* ToDo: check 2nd arg (mvar) is right */
+      mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+#else
+      mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
+#endif
+      if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+         mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+      }
+#ifdef SMP
+      /* unlocks the MVar in the SMP case */
+      SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+#endif
+      JMP_(ENTRY_CODE(Sp[0]));
+  } else {
+      /* No further takes, the MVar is now full. */
+      mvar->value = R2.cl;
+      /* unlocks the MVar in the SMP case */
+      SET_INFO(mvar,&stg_FULL_MVAR_info);
+      JMP_(ENTRY_CODE(Sp[0]));
+  }
+
+  /* ToDo: yield afterward for better communication performance? */
   FE_
 }
 
@@ -996,7 +1189,7 @@ FN_(makeStableNamezh_fast)
   /* Is there already a StableName for this heap object? */
   if (stable_ptr_table[index].sn_obj == NULL) {
     sn_obj = (StgStableName *) (Hp - sizeofW(StgStableName) + 1);
-    sn_obj->header.info = &stg_STABLE_NAME_info;
+    SET_HDR(sn_obj,&stg_STABLE_NAME_info,CCCS);
     sn_obj->sn = index;
     stable_ptr_table[index].sn_obj = (StgClosure *)sn_obj;
   } else {