=== modified file 'src/ca/Makefile'
--- src/ca/Makefile	2008-08-28 21:16:52 +0000
+++ src/ca/Makefile	2011-10-19 20:04:54 +0000
@@ -92,7 +92,7 @@
 PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32
 
 PROD_HOST += caRepeater catime acctst caConnTest casw caEventRate
-OBJS_IOC_vxWorks += catime acctst caConnTest casw caEventRate
+OBJS_IOC += catime acctst caConnTest casw caEventRate acctstRegister
 caRepeater_SRCS = caRepeater.cpp
 catime_SRCS = catimeMain.c catime.c 
 acctst_SRCS = acctstMain.c acctst.c 

=== modified file 'src/ca/acctst.c'
--- src/ca/acctst.c	2009-08-31 19:47:58 +0000
+++ src/ca/acctst.c	2011-10-19 20:04:54 +0000
@@ -10,6 +10,10 @@
 
 /*
  * CA regression test
+ * Authors:
+ * Jeff Hill
+ * Murali Shankar - initial versions of verifyMultithreadSubscr
+ * 
  */
 
 /*
@@ -26,6 +30,8 @@
  */
 #define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
 #include "epicsAssert.h"
+#include "epicsMutex.h"
+#include "epicsEvent.h"
 #include "epicsTime.h"
 #include "dbDefs.h"
 #include "envDefs.h"
@@ -2719,6 +2725,186 @@
     }
 }
 
+typedef struct {
+    char m_chanName[100u];
+    struct ca_client_context * m_pCtx;
+    chid m_chan;
+    epicsMutexId m_mutex;
+    epicsEventId m_testCompleteEvent;
+    epicsEventId m_threadExitEvent;
+    size_t m_nUpdatesReceived;
+    size_t m_nUpdatesRequired;
+    int m_testInitiated;
+    int m_testComplete;
+    unsigned m_interestLevel;
+} MultiThreadSubscrTest;
+
+static void testMultithreadSubscrSubscrCallback 
+                        ( struct event_handler_args eha )
+{
+    const epicsEventId firstUpdateEvent = ( epicsEventId ) eha.usr;
+    epicsEventSignal ( firstUpdateEvent );
+}
+
+static void testMultithreadSubscrCreateSubscr ( void * pParm ) 
+{
+    static unsigned nElem = 0;
+    int testComplete = FALSE;
+    evid id;
+    epicsEventId firstUpdateEvent;
+    epicsEventWaitStatus eventWaitStatus;   
+    MultiThreadSubscrTest * const pMultiThreadSubscrTest = 
+                                ( MultiThreadSubscrTest * ) pParm;
+
+    /* this is required for the ca_flush below to work correctly */
+    int status = ca_attach_context ( pMultiThreadSubscrTest->m_pCtx );
+    verify ( status == ECA_NORMAL );
+    firstUpdateEvent = epicsEventMustCreate ( epicsEventEmpty );
+    verify ( firstUpdateEvent );
+    status = ca_create_subscription ( 
+                            DBR_TIME_LONG, 
+                            nElem, 
+                            pMultiThreadSubscrTest->m_chan, 
+                            DBE_VALUE, 
+                            testMultithreadSubscrSubscrCallback, 
+                            firstUpdateEvent, 
+                            & id );
+    verify ( status == ECA_NORMAL );
+    status = ca_flush_io ();
+    verify ( status == ECA_NORMAL );
+    /* wait for first update */
+    eventWaitStatus = epicsEventWaitWithTimeout ( 
+                            firstUpdateEvent, 60.0 * 10 );
+    verify ( eventWaitStatus ==  epicsEventWaitOK );
+    epicsEventDestroy ( firstUpdateEvent );
+    status = ca_clear_subscription ( id );
+    verify ( status == ECA_NORMAL );
+    epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex );
+    pMultiThreadSubscrTest->m_nUpdatesReceived++;
+    testComplete = ( pMultiThreadSubscrTest->m_nUpdatesReceived == 
+                    pMultiThreadSubscrTest->m_nUpdatesRequired );
+    pMultiThreadSubscrTest->m_testComplete = testComplete;
+    epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex );
+    if ( testComplete ) {
+        epicsEventSignal ( pMultiThreadSubscrTest->m_testCompleteEvent );
+    }
+}
+
+void testMultithreadSubscrConnHandler ( struct connection_handler_args args )
+{
+    MultiThreadSubscrTest * const pMultiThreadSubscrTest = 
+                        ( MultiThreadSubscrTest * ) ca_puser ( args.chid );
+    epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex );
+    if ( !pMultiThreadSubscrTest->m_testInitiated && 
+            args.op == CA_OP_CONN_UP ) {
+        int i;
+        pMultiThreadSubscrTest->m_testInitiated = TRUE;
+        for ( i = 0; i < pMultiThreadSubscrTest->m_nUpdatesRequired; i++ ) {
+            char threadname[64];
+            epicsThreadId threadId;
+            sprintf(threadname, "testSubscr%06u", i);
+            threadId = epicsThreadCreate ( threadname, 
+                        epicsThreadPriorityMedium, 
+                        epicsThreadGetStackSize(epicsThreadStackSmall), 
+                        testMultithreadSubscrCreateSubscr, 
+                        pMultiThreadSubscrTest );
+            verify ( threadId );
+        }
+    }
+    epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex );
+}
+
+void testMultithreadSubscr ( void * pParm )
+{
+    MultiThreadSubscrTest * const pMultiThreadSubscrTest = 
+                                ( MultiThreadSubscrTest * ) pParm;
+    int status;
+    unsigned i;
+
+    status = ca_context_create ( ca_enable_preemptive_callback );
+    verify ( status == ECA_NORMAL );
+    pMultiThreadSubscrTest->m_pCtx = ca_current_context ();
+    verify ( pMultiThreadSubscrTest->m_pCtx );
+    status = ca_create_channel (
+                pMultiThreadSubscrTest->m_chanName,
+                testMultithreadSubscrConnHandler,
+                pMultiThreadSubscrTest,
+                CA_PRIORITY_MIN, 
+                & pMultiThreadSubscrTest->m_chan );
+    verify ( status == ECA_NORMAL );
+
+    showProgressBegin ( "verifyMultithreadSubscr", 
+            pMultiThreadSubscrTest->m_interestLevel );
+    i = 0;
+    while ( TRUE ) {
+        int success = FALSE;
+        epicsEventWaitStatus eventWaitStatus;
+        epicsMutexMustLock ( pMultiThreadSubscrTest->m_mutex );
+        success = pMultiThreadSubscrTest->m_testComplete;
+        epicsMutexUnlock ( pMultiThreadSubscrTest->m_mutex );
+        if ( success ) {
+            break;
+        }
+        eventWaitStatus = epicsEventWaitWithTimeout ( 
+                            pMultiThreadSubscrTest->m_testCompleteEvent, 0.1 );
+        verify ( eventWaitStatus ==  epicsEventWaitOK || 
+                eventWaitStatus ==  epicsEventWaitTimeout );
+        if ( i++ % 100 == 0u ) 
+            showProgress ( pMultiThreadSubscrTest->m_interestLevel );
+        verify ( i < 1000 );
+    }
+    showProgressEnd ( pMultiThreadSubscrTest->m_interestLevel );
+
+    status = ca_clear_channel ( pMultiThreadSubscrTest->m_chan );
+    verify ( status == ECA_NORMAL );
+    ca_context_destroy ();
+    epicsEventSignal ( pMultiThreadSubscrTest->m_threadExitEvent );
+}
+
+/*
+ * test installation of subscriptions similar to usage paterns
+ * employed by modern versions of the sequencer
+ */
+void verifyMultithreadSubscr ( const char * pName, unsigned interestLevel )
+{
+    static unsigned nSubscr = 3000;
+    epicsThreadId threadId;
+    MultiThreadSubscrTest * const pMultiThreadSubscrTest = 
+                                (MultiThreadSubscrTest*) calloc ( 1, 
+                                    sizeof ( MultiThreadSubscrTest ) );
+    verify ( pMultiThreadSubscrTest);
+    pMultiThreadSubscrTest->m_mutex = epicsMutexMustCreate ();
+    verify ( pMultiThreadSubscrTest->m_mutex );
+    pMultiThreadSubscrTest->m_testCompleteEvent = 
+                epicsEventMustCreate ( epicsEventEmpty );
+    verify ( pMultiThreadSubscrTest->m_testCompleteEvent );
+    pMultiThreadSubscrTest->m_threadExitEvent = 
+                epicsEventMustCreate ( epicsEventEmpty );
+    verify ( pMultiThreadSubscrTest->m_threadExitEvent );
+    strncpy ( pMultiThreadSubscrTest->m_chanName, pName, 
+            sizeof ( pMultiThreadSubscrTest->m_chanName ) );
+    pMultiThreadSubscrTest->m_chanName
+        [ sizeof ( pMultiThreadSubscrTest->m_chanName ) - 1u ] = '\0';
+    pMultiThreadSubscrTest->m_nUpdatesRequired = nSubscr;
+    pMultiThreadSubscrTest->m_interestLevel = interestLevel;
+    threadId = epicsThreadCreate ( 
+                "testMultithreadSubscr", 
+                epicsThreadPriorityMedium, 
+                epicsThreadGetStackSize(epicsThreadStackSmall), 
+                testMultithreadSubscr, pMultiThreadSubscrTest );
+    verify ( threadId );
+    {
+        epicsEventWaitStatus eventWaitStatus;
+        eventWaitStatus = epicsEventWaitWithTimeout ( 
+                pMultiThreadSubscrTest->m_threadExitEvent, 1000.0 );
+        verify  ( eventWaitStatus == epicsEventWaitOK );
+    }
+    epicsEventDestroy ( pMultiThreadSubscrTest->m_testCompleteEvent );
+    epicsEventDestroy ( pMultiThreadSubscrTest->m_threadExitEvent );
+    epicsMutexDestroy ( pMultiThreadSubscrTest->m_mutex );
+    free ( pMultiThreadSubscrTest );
+}
+
 void fdManagerVerify ( const char * pName, unsigned interestLevel )
 {
     int status;
@@ -2965,17 +3151,22 @@
             SEVCHK ( status, "context create failed" );
             
             status = ca_create_channel  ( pName, 0, 0, 0, & chan );
-            SEVCHK ( status, NULL );
-            
-            status = ca_pend_io( timeoutToPendIO );
-            SEVCHK ( status, "channel connect failed" );
-            
-            status = ca_put ( DBR_DOUBLE, chan, & stim );
-            SEVCHK ( status, "channel put failed" );
-    
-            status = ca_clear_channel ( chan );
-            SEVCHK ( status, NULL );
-    
+            /*
+             * currently in-memory channels cant be used with this test
+             * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!!
+             */
+            if ( status != ECA_UNAVAILINSERV ) {
+                SEVCHK ( status, NULL );
+                
+                status = ca_pend_io( timeoutToPendIO );
+                SEVCHK ( status, "channel connect failed" );
+                
+                status = ca_put ( DBR_DOUBLE, chan, & stim );
+                SEVCHK ( status, "channel put failed" );
+        
+                status = ca_clear_channel ( chan );
+                SEVCHK ( status, NULL );
+            }
             ca_context_destroy ();
         }
         
@@ -2985,24 +3176,28 @@
             dbr_double_t resp;
             status = ca_context_create ( ca_disable_preemptive_callback );
             SEVCHK ( status, "context create failed" );
-            
+           
             status = ca_create_channel  ( pName, 0, 0, 0, & chan );
             SEVCHK ( status, NULL );
-            
-            status = ca_pend_io( timeoutToPendIO );
-            SEVCHK ( status, "channel connect failed" );
-    
-            status = ca_get ( DBR_DOUBLE, chan, & resp );
-            SEVCHK ( status, "channel get failed" );
-    
-            status = ca_pend_io ( timeoutToPendIO );
-            SEVCHK ( status, "get, pend io failed" );
-            
-            verify ( stim == resp );
-    
-            status = ca_clear_channel ( chan );
-            SEVCHK ( status, NULL );
-    
+            /*
+             * currently in-memory channels cant be used with this test
+             * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!!
+             */
+            if ( status != ECA_UNAVAILINSERV ) {
+                status = ca_pend_io( timeoutToPendIO );
+                SEVCHK ( status, "channel connect failed" );
+        
+                status = ca_get ( DBR_DOUBLE, chan, & resp );
+                SEVCHK ( status, "channel get failed" );
+        
+                status = ca_pend_io ( timeoutToPendIO );
+                SEVCHK ( status, "get, pend io failed" );
+                
+                verify ( stim == resp );
+        
+                status = ca_clear_channel ( chan );
+                SEVCHK ( status, NULL );
+            }
             ca_context_destroy ();
         }
 
@@ -3028,6 +3223,13 @@
     
     for ( i = 0; i < NELEMENTS ( chan ); i++ ) {
         status = ca_create_channel  ( pName, 0, 0, 0, & chan[i] );
+        /*
+         * currently in-memory channels cant be used with this test
+         * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!!
+         */
+        if ( status == ECA_UNAVAILINSERV ) {
+            break;
+        }
         SEVCHK ( status, NULL );
     }
     
@@ -3123,6 +3325,7 @@
     verifyHighThroughputReadCallback ( chan, interestLevel );
     verifyHighThroughputWriteCallback ( chan, interestLevel );
     verifyBadString ( chan, interestLevel );
+    verifyMultithreadSubscr ( pName, interestLevel );
     if ( select != ca_enable_preemptive_callback ) {
         fdManagerVerify ( pName, interestLevel ); 
     }

=== added file 'src/ca/acctstRegister.cpp'
--- src/ca/acctstRegister.cpp	1970-01-01 00:00:00 +0000
+++ src/ca/acctstRegister.cpp	2011-10-19 22:34:51 +0000
@@ -0,0 +1,69 @@
+/*************************************************************************\
+* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ * CA client library diagnostics IOC shell registration
+ * Authors:
+ * Jeff Hill
+ */
+
+#include <iocsh.h>
+#include "caDiagnostics.h"
+
+/* Information needed by iocsh */
+static const iocshArg     acctstArg0 = { "channel name", iocshArgString };
+static const iocshArg     acctstArg1 = { "interest level", iocshArgInt };
+static const iocshArg     acctstArg2 = { "channel count", iocshArgInt };
+static const iocshArg     acctstArg3 = { "repetition count", iocshArgInt };
+static const iocshArg     acctstArg4 = { "preemptive callback select", iocshArgInt };
+
+static const iocshArg    *acctstArgs[] =
+{
+    &acctstArg0,
+    &acctstArg1,
+    &acctstArg2,
+    &acctstArg3,
+    &acctstArg4
+};
+static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs};
+
+
+/* Wrapper called by iocsh, selects the argument types that print needs */
+static void acctstCallFunc(const iocshArgBuf *args) {
+    if ( args[1].ival <  0 ) {
+        printf ( "negative interest level not allowed\n" );
+        return;
+    }
+    if ( args[2].ival <  0 ) {
+        printf ( "negative channel count not allowed\n" );
+        return;
+    }
+    if ( args[3].ival <  0 ) {
+        printf ( "negative repetition count not allowed\n" );
+        return;
+    }
+    acctst (
+        args[0].sval, /* channel name */
+        ( unsigned ) args[1].ival, /* interest level */
+        ( unsigned ) args[2].ival, /* channel count */
+        ( unsigned ) args[3].ival, /* repetition count */
+        ( ca_preemptive_callback_select ) args[4].ival );  /* preemptive callback select */
+}
+
+struct AutoInit {
+    AutoInit ();
+};
+
+AutoInit :: AutoInit () 
+{
+    iocshRegister ( &acctstFuncDef, acctstCallFunc );
+}
+
+AutoInit autoInit;
+

=== modified file 'src/ca/cadef.h'
--- src/ca/cadef.h	2009-06-05 19:56:33 +0000
+++ src/ca/cadef.h	2011-10-19 20:04:54 +0000
@@ -185,7 +185,8 @@
 epicsShareFunc int epicsShareAPI ca_task_initialize (void);
 enum ca_preemptive_callback_select 
 { ca_disable_preemptive_callback, ca_enable_preemptive_callback };
-epicsShareFunc int epicsShareAPI ca_context_create (enum ca_preemptive_callback_select select);
+epicsShareFunc int epicsShareAPI 
+        ca_context_create (enum ca_preemptive_callback_select select);
 epicsShareFunc void epicsShareAPI ca_detach_context (); 
 
 /************************************************************************/

=== modified file 'src/db/dbEvent.c'
--- src/db/dbEvent.c	2010-10-05 19:27:37 +0000
+++ src/db/dbEvent.c	2011-10-19 20:04:54 +0000
@@ -203,35 +203,44 @@
             }
 
             if ( level > 1 ) {
-                unsigned nEntriesFree = ringSpace ( pevent->ev_que );
+                unsigned nEntriesFree;
+                const void * taskId;
+                LOCKEVQUE(pevent->ev_que);
+                nEntriesFree = ringSpace ( pevent->ev_que );
+                taskId = ( void * ) pevent->ev_que->evUser->taskid;
+                UNLOCKEVQUE(pevent->ev_que);
                 if ( nEntriesFree == 0u ) {
                     printf ( ", thread=%p, queue full", 
-                        (void *) pevent->ev_que->evUser->taskid );
+                        (void *) taskId );
                 }
                 else if ( nEntriesFree == EVENTQUESIZE ) {
                     printf ( ", thread=%p, queue empty", 
-                        (void *) pevent->ev_que->evUser->taskid );
+                        (void *) taskId );
                 }
                 else {
                     printf ( ", thread=%p, unused entries=%u", 
-                        (void *) pevent->ev_que->evUser->taskid, nEntriesFree );
+                        (void *) taskId, nEntriesFree );
                 }
             }
 
             if ( level > 2 ) {
+                unsigned nDuplicates;
+                unsigned nCanceled;
                 if ( pevent->nreplace ) {
                     printf (", discarded by replacement=%ld", pevent->nreplace);
                 }
                 if ( ! pevent->valque ) {
                     printf (", queueing disabled" );
                 }
-                if  ( pevent->ev_que->nDuplicates ) {
-                    printf (", duplicate count =%u\n", 
-                        pevent->ev_que->nDuplicates );
+                LOCKEVQUE(pevent->ev_que);
+                nDuplicates = pevent->ev_que->nDuplicates;
+                nCanceled = pevent->ev_que->nCanceled;
+                UNLOCKEVQUE(pevent->ev_que);
+                if  ( nDuplicates ) {
+                    printf (", duplicate count =%u\n", nDuplicates );
                 }
-                if ( pevent->ev_que->nCanceled ) {
-                    printf (", canceled count =%u\n", 
-                        pevent->ev_que->nCanceled );
+                if ( nCanceled ) {
+                    printf (", canceled count =%u\n", nCanceled );
                 }
             }
 
@@ -264,7 +273,7 @@
  */
 dbEventCtx epicsShareAPI db_init_events (void)
 {
-    struct event_user   *evUser;
+    struct event_user * evUser;
     
     if (!dbevEventUserFreeList) {
         freeListInitPvt(&dbevEventUserFreeList, 
@@ -326,7 +335,7 @@
  */
 void epicsShareAPI db_close_events (dbEventCtx ctx)
 {
-    struct event_user *evUser = (struct event_user *) ctx;
+    struct event_user * const evUser = (struct event_user *) ctx;
 
     /*
      * Exit not forced on event blocks for now - this is left to channel
@@ -336,27 +345,42 @@
      * NOTE: not deleting events before calling this routine could be
      * hazardous to the system's health.
      */
-
+    epicsMutexMustLock ( evUser->lock );
     evUser->pendexit = TRUE;
-
+    epicsMutexUnlock ( evUser->lock );
     /* notify the waiting task */
     epicsEventSignal(evUser->ppendsem);
 }
 
 /*
+ * create_ev_que()
+ */
+static struct event_que * create_ev_que ( struct event_user * const evUser )
+{
+    struct event_que * const ev_que = (struct event_que *) 
+        freeListCalloc ( dbevEventQueueFreeList );
+    if ( ! ev_que ) {
+        return NULL;
+    }
+    ev_que->writelock = epicsMutexCreate();
+    if ( ! ev_que->writelock ) {
+        freeListFree ( dbevEventQueueFreeList, ev_que );
+        return NULL;
+    }
+    ev_que->evUser = evUser;
+    return ev_que;
+}
+
+/*
  * DB_ADD_EVENT()
  */
 dbEventSubscription epicsShareAPI db_add_event (
     dbEventCtx ctx, struct dbAddr *paddr,
     EVENTFUNC *user_sub, void *user_arg, unsigned select)
 {
-    struct event_user   *evUser = (struct event_user *) ctx;
-    struct dbCommon     *precord;
-    struct event_que    *ev_que;
-    struct event_que    *tmp_que;
-    struct evSubscrip  *pevent;
-
-    precord = paddr->precord;
+    struct event_user * const evUser = (struct event_user *) ctx;
+    struct event_que * ev_que;
+    struct evSubscrip * pevent;
 
     /*
      * Don't add events which will not be triggered
@@ -365,38 +389,42 @@
         return NULL;
     }
 
-    pevent = freeListCalloc (dbevEventBlockFreeList);
-    if (!pevent) {
+    pevent = freeListCalloc ( dbevEventBlockFreeList );
+    if ( ! pevent ) {
         return NULL;
     }
 
     /* find an event que block with enough quota */
     /* otherwise add a new one to the list */
-    ev_que = &evUser->firstque;
-    while (TRUE) {
-        if (ev_que->quota + ev_que->nCanceled < EVENTQUESIZE - EVENTENTRIES) {
-            break;
-        }
-        if (!ev_que->nextque) {
-            tmp_que = (struct event_que *) 
-                freeListCalloc(dbevEventQueueFreeList);
-            if (!tmp_que) {
-                freeListFree (dbevEventBlockFreeList, pevent);
-                return NULL;
-            }
-            tmp_que->evUser = evUser;
-            tmp_que->writelock = epicsMutexCreate();
-            if (!tmp_que->writelock) {
-                freeListFree (dbevEventBlockFreeList, pevent);
-                freeListFree (dbevEventQueueFreeList, tmp_que);
-                return NULL;
-            }
-            ev_que->nextque = tmp_que;
-            ev_que = tmp_que;
-            break;
+    epicsMutexMustLock ( evUser->lock );
+    ev_que = & evUser->firstque;
+    while ( TRUE ) {
+        int success = 0;
+        LOCKEVQUE ( ev_que );
+        success = ( ev_que->quota + ev_que->nCanceled < 
+                                EVENTQUESIZE - EVENTENTRIES );
+        if ( success ) {
+            ev_que->quota += EVENTENTRIES;
+        }
+        UNLOCKEVQUE ( ev_que );
+        if ( success ) {
+            break;
+        }
+        if ( ! ev_que->nextque ) {
+            ev_que->nextque = create_ev_que ( evUser );
+            if ( ! ev_que->nextque ) {
+                ev_que = NULL;
+                break;
+            }
         }
         ev_que = ev_que->nextque;
     }
+    epicsMutexUnlock ( evUser->lock );
+
+    if ( ! ev_que ) {
+        freeListFree ( dbevEventBlockFreeList, pevent );
+        return NULL;
+    }
 
     pevent->npend =     0ul;
     pevent->nreplace =  0ul;
@@ -409,10 +437,6 @@
     pevent->enabled =   FALSE;
     pevent->ev_que =    ev_que;
 
-    LOCKEVQUE(ev_que);
-    ev_que->quota += EVENTENTRIES;
-    UNLOCKEVQUE(ev_que);
-
     /*
      * Simple types values queued up for reliable interprocess
      * communication (for other types they get whatever happens to be
@@ -434,10 +458,9 @@
  */
 void epicsShareAPI db_event_enable (dbEventSubscription es)
 {
-    struct evSubscrip *pevent = (struct evSubscrip *) es;
-    struct dbCommon *precord;
-
-    precord = (struct dbCommon *) pevent->paddr->precord;
+    struct evSubscrip * const pevent = (struct evSubscrip *) es;
+    struct dbCommon * const precord = 
+                (struct dbCommon *) pevent->paddr->precord;
 
     LOCKREC(precord);
     if ( ! pevent->enabled ) {
@@ -452,10 +475,9 @@
  */
 void epicsShareAPI db_event_disable (dbEventSubscription es)
 {
-    struct evSubscrip *pevent = (struct evSubscrip *) es;
-    struct dbCommon *precord;
-
-    precord = (struct dbCommon *) pevent->paddr->precord;
+    struct evSubscrip * const pevent = (struct evSubscrip *) es;
+    struct dbCommon * const precord = 
+                (struct dbCommon *) pevent->paddr->precord;
 
     LOCKREC(precord);
     if ( pevent->enabled ) {
@@ -472,7 +494,7 @@
 static void event_remove ( struct event_que *ev_que, 
     unsigned short index, struct evSubscrip *placeHolder )
 {
-    struct evSubscrip *pEvent = ev_que->evque[index];
+    struct evSubscrip * const pEvent = ev_que->evque[index];
 
     ev_que->evque[index] = placeHolder;
     if ( pEvent->npend == 1u ) {
@@ -495,12 +517,9 @@
  */
 void epicsShareAPI db_cancel_event (dbEventSubscription es)
 {
-    struct evSubscrip * pevent = ( struct evSubscrip * ) es;
-    struct dbCommon * precord;
+    struct evSubscrip * const pevent = ( struct evSubscrip * ) es;
     unsigned short getix;
 
-    precord = ( struct dbCommon * ) pevent->paddr->precord;
-
     db_event_disable ( es );
 
     /*
@@ -564,12 +583,12 @@
  */
 void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx)
 {
-    struct event_user *evUser = (struct event_user *) ctx;
+    struct event_user * const evUser = (struct event_user *) ctx;
 
 	epicsMutexMustLock ( evUser->lock );
     while ( evUser->extraLaborBusy ) {
         epicsMutexUnlock ( evUser->lock );
-        epicsThreadSleep(1.0);
+        epicsThreadSleep(0.1);
         epicsMutexMustLock ( evUser->lock );
     }
     epicsMutexUnlock ( evUser->lock );
@@ -585,7 +604,7 @@
 int epicsShareAPI db_add_extra_labor_event (
     dbEventCtx ctx, EXTRALABORFUNC *func, void *arg)
 {
-    struct event_user *evUser = (struct event_user *) ctx;
+    struct event_user * const evUser = (struct event_user *) ctx;
 
     epicsMutexMustLock ( evUser->lock );
     evUser->extralabor_sub = func;
@@ -600,7 +619,7 @@
  */
 int epicsShareAPI db_post_extra_labor (dbEventCtx ctx)
 {
-    struct event_user *evUser = (struct event_user *) ctx;
+    struct event_user * const evUser = (struct event_user *) ctx;
     int doit;
 
     epicsMutexMustLock ( evUser->lock );
@@ -627,12 +646,10 @@
  */
 static void db_post_single_event_private (struct evSubscrip *event)
 {  
-    struct event_que    *ev_que;
-    db_field_log        *pLog;
-    int                 firstEventFlag;
-    unsigned            rngSpace;
-
-    ev_que = event->ev_que;
+    struct event_que * const ev_que = event->ev_que;
+    db_field_log * pLog;
+    int firstEventFlag;
+    unsigned rngSpace;
 
     /*
      * evUser ring buffer must be locked for the multiple
@@ -747,10 +764,10 @@
 unsigned int    caEventMask
 )
 {  
-    struct dbCommon     *pdbc = (struct dbCommon *)pRecord;
-    struct evSubscrip   *event;
+    struct dbCommon * const pdbc = (struct dbCommon *)pRecord;
+    struct evSubscrip * event;
 
-    if (pdbc->mlis.count == 0) return DB_EVENT_OK;       /* no monitors set */
+    if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */
 
     LOCKREC(pdbc);
   
@@ -762,8 +779,7 @@
          * changed or pval==NULL and waiting on alarms and alarms changed
          */
         if ( (event->paddr->pfield == (void *)pField || pField==NULL) &&
-            (caEventMask & event->select)) {
-
+            (caEventMask & event->select) ) {
             db_post_single_event_private (event);
         }
     }
@@ -778,8 +794,8 @@
  */
 void epicsShareAPI db_post_single_event (dbEventSubscription es)
 {  
-    struct evSubscrip *event = (struct evSubscrip *) es;
-    struct dbCommon *precord = event->paddr->precord;
+    struct evSubscrip * const event = (struct evSubscrip *) es;
+    struct dbCommon * const precord = event->paddr->precord;
 
     dbScanLock (precord);
     db_post_single_event_private (event);
@@ -898,8 +914,8 @@
  */
 static void event_task (void *pParm)
 {
-    struct event_user   *evUser = (struct event_user *) pParm;
-    struct event_que    *ev_que;
+    struct event_user * const evUser = (struct event_user *) pParm;
+    struct event_que * ev_que;
 
     /* init hook */
     if (evUser->init_func) {
@@ -934,12 +950,14 @@
             epicsMutexMustLock ( evUser->lock );
         }
         evUser->extraLaborBusy = FALSE;
-        epicsMutexUnlock ( evUser->lock );
 
         for ( ev_que = &evUser->firstque; ev_que; 
                 ev_que = ev_que->nextque ) {
+            epicsMutexUnlock ( evUser->lock );
             event_read (ev_que);
+            epicsMutexMustLock ( evUser->lock );
         }
+        epicsMutexUnlock ( evUser->lock );
 
     } while( ! evUser->pendexit );
 
@@ -975,7 +993,7 @@
     dbEventCtx ctx,const char *taskname, void (*init_func)(void *), 
     void *init_func_arg, unsigned osiPriority )
 {
-     struct event_user *evUser = (struct event_user *) ctx;
+     struct event_user * const evUser = (struct event_user *) ctx;
      
      epicsMutexMustLock ( evUser->lock );
 
@@ -995,7 +1013,8 @@
          taskname = EVENT_PEND_NAME;
      }
      evUser->taskid = epicsThreadCreate (
-         taskname, osiPriority, epicsThreadGetStackSize(epicsThreadStackMedium),
+         taskname, osiPriority, 
+         epicsThreadGetStackSize(epicsThreadStackMedium),
          event_task, (void *)evUser);
      if (!evUser->taskid) {
          epicsMutexUnlock ( evUser->lock );
@@ -1008,9 +1027,10 @@
 /*
  * db_event_change_priority()
  */
-void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority )
+void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, 
+                                        unsigned epicsPriority )
 {
-    struct event_user * evUser = ( struct event_user * ) ctx;
+    struct event_user * const evUser = ( struct event_user * ) ctx;
     epicsThreadSetPriority ( evUser->taskid, epicsPriority );
 }
 
@@ -1019,9 +1039,11 @@
  */
 void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx)
 {
-    struct event_user *evUser = (struct event_user *) ctx;
+    struct event_user * const evUser = (struct event_user *) ctx;
 
+    epicsMutexMustLock ( evUser->lock );
     evUser->flowCtrlMode = TRUE;
+    epicsMutexUnlock ( evUser->lock );
     /* 
      * notify the event handler task
      */
@@ -1036,9 +1058,11 @@
  */
 void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx)
 {
-    struct event_user *evUser = (struct event_user *) ctx;
+    struct event_user * const evUser = (struct event_user *) ctx;
 
+    epicsMutexMustLock ( evUser->lock );
     evUser->flowCtrlMode = FALSE;
+    epicsMutexUnlock ( evUser->lock );
     /* 
      * notify the event handler task
      */

=== modified file 'src/db/dbPutNotifyBlocker.cpp'
--- src/db/dbPutNotifyBlocker.cpp	2010-10-05 19:27:37 +0000
+++ src/db/dbPutNotifyBlocker.cpp	2011-10-19 20:04:54 +0000
@@ -74,7 +74,7 @@
     epicsGuard < epicsMutex > & guard )
 {
     guard.assertIdenticalMutex ( this->mutex );
-    if ( this->pn.paddr ) {
+    if ( this->pNotify ) {
         epicsGuardRelease < epicsMutex > unguard ( guard );
         dbNotifyCancel ( &this->pn );
     }
@@ -100,26 +100,31 @@
 
 extern "C" void putNotifyCompletion ( putNotify *ppn )
 {
-    dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt );
-    {
-        epicsGuard < epicsMutex > guard ( pBlocker->mutex );
-        if ( pBlocker->pNotify ) {
-            if ( pBlocker->pn.status != putNotifyOK) {
-                pBlocker->pNotify->exception ( 
-                    guard, ECA_PUTFAIL,  "put notify unsuccessful",
-                    static_cast <unsigned> (pBlocker->pn.dbrType), 
-                    static_cast <unsigned> (pBlocker->pn.nRequest) );
-            }
-            else {
-                pBlocker->pNotify->completion ( guard );
-            }
-        }
-        else {
-            errlogPrintf ( "put notify completion with nill pNotify?\n" );
-        }
+    dbPutNotifyBlocker * const pBlocker = 
+            static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt );
+    epicsGuard < epicsMutex > guard ( pBlocker->mutex );
+    cacWriteNotify * const pNtfy = pBlocker->pNotify;
+    if ( pNtfy ) {
         pBlocker->pNotify = 0;
-    }
-    pBlocker->block.signal ();
+        // Its necessary to signal the initiators now before we call
+        // the user callback. This is less efficent, and potentially
+        // causes more thread context switching, but its probably 
+        // unavoidable because its possible that the use callback 
+        // might destroy this object.
+        pBlocker->block.signal ();
+        if ( pBlocker->pn.status != putNotifyOK ) {
+            pNtfy->exception ( 
+                guard, ECA_PUTFAIL,  "put notify unsuccessful",
+                static_cast < unsigned > (pBlocker->pn.dbrType), 
+                static_cast < unsigned > (pBlocker->pn.nRequest) );
+        }
+        else {
+            pNtfy->completion ( guard );
+        }
+    }
+    else {
+        errlogPrintf ( "put notify completion with nill pNotify?\n" );
+    }
 }
 
 void dbPutNotifyBlocker::initiatePutNotify ( 

