Fixed Asynchronous Event Queueing to work correctly when doing Lock based queueing

This commit is contained in:
Mark Pizzolato 2013-01-10 09:53:40 -08:00
parent 629de4dcc5
commit bc816ae871
3 changed files with 20 additions and 8 deletions

View file

@ -485,7 +485,7 @@ DEBTAB cpu_deb[] = {
{ "CONTEXT", LOG_CPU_P }, { "CONTEXT", LOG_CPU_P },
{ "EVENT", SIM_DBG_EVENT }, { "EVENT", SIM_DBG_EVENT },
{ "ACTIVATE", SIM_DBG_ACTIVATE }, { "ACTIVATE", SIM_DBG_ACTIVATE },
{ "QUEUE", SIM_DBG_AIO_QUEUE }, { "ASYNCH", SIM_DBG_AIO_QUEUE },
{ NULL, 0 } { NULL, 0 }
}; };

3
scp.c
View file

@ -1921,7 +1921,7 @@ t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cp
if (cptr && (*cptr != 0)) if (cptr && (*cptr != 0))
return SCPE_2MARG; return SCPE_2MARG;
#ifdef SIM_ASYNCH_IO #ifdef SIM_ASYNCH_IO
fprintf (st, "Asynchronous I/O is %sabled\n", (sim_asynch_enabled) ? "en" : "dis"); fprintf (st, "Asynchronous I/O is %sabled, %s\n", (sim_asynch_enabled) ? "en" : "dis", AIO_QUEUE_MODE);
#else #else
fprintf (st, "Asynchronous I/O is not available in this simulator\n"); fprintf (st, "Asynchronous I/O is not available in this simulator\n");
#endif #endif
@ -5438,6 +5438,7 @@ if (stop_cpu) /* stop CPU? */
return SCPE_STOP; return SCPE_STOP;
AIO_UPDATE_QUEUE; AIO_UPDATE_QUEUE;
UPDATE_SIM_TIME; /* update sim time */ UPDATE_SIM_TIME; /* update sim time */
if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */ if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */
sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Emptry New Interval = %d\n", sim_interval); sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Emptry New Interval = %d\n", sim_interval);

View file

@ -654,9 +654,12 @@ extern int32 sim_asynch_inst_latency;
#define AIO_IS_ACTIVE(uptr) (((uptr)->a_is_active ? (uptr)->a_is_active (uptr) : FALSE) || ((uptr)->a_next)) #define AIO_IS_ACTIVE(uptr) (((uptr)->a_is_active ? (uptr)->a_is_active (uptr) : FALSE) || ((uptr)->a_next))
#define AIO_CANCEL(uptr) if ((uptr)->a_cancel) (uptr)->a_cancel (uptr); else (void)0 #define AIO_CANCEL(uptr) if ((uptr)->a_cancel) (uptr)->a_cancel (uptr); else (void)0
#define AIO_LOCK \ #define AIO_LOCK \
pthread_mutex_lock(&sim_asynch_lock) int _locked = pthread_mutex_trylock(&sim_asynch_lock)
#define AIO_UNLOCK \ #define AIO_UNLOCK \
pthread_mutex_unlock(&sim_asynch_lock) if (_locked == 0) \
pthread_mutex_unlock(&sim_asynch_lock); \
else \
(void)0
#if defined(__DECC_VER) #if defined(__DECC_VER)
#include <builtins> #include <builtins>
@ -667,6 +670,11 @@ extern int32 sim_asynch_inst_latency;
#if defined(_WIN32) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) #if defined(_WIN32) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)
#define USE_AIO_INTRINSICS 1 #define USE_AIO_INTRINSICS 1
#endif #endif
/* Provide a way to test both Intrinsic and Lock based queue manipulations */
/* when both are available on a particular platform */
#if defined(DONT_USE_AIO_INTRINSICS) && defined(USE_AIO_INTRINSICS)
#undef USE_AIO_INTRINSICS
#endif
#ifdef USE_AIO_INTRINSICS #ifdef USE_AIO_INTRINSICS
/* This approach uses intrinsics to manage access to the link list head */ /* This approach uses intrinsics to manage access to the link list head */
/* sim_asynch_queue. This implementation is a completely lock free design */ /* sim_asynch_queue. This implementation is a completely lock free design */
@ -686,6 +694,7 @@ extern int32 sim_asynch_inst_latency;
#else #else
#error "Implementation of function InterlockedCompareExchangePointer() is needed to build with USE_AIO_INTRINSICS" #error "Implementation of function InterlockedCompareExchangePointer() is needed to build with USE_AIO_INTRINSICS"
#endif #endif
#define AIO_QUEUE_MODE "Lock free asynchronous event Queue access"
#define AIO_QUEUE_VAL InterlockedCompareExchangePointer(&sim_asynch_queue, sim_asynch_queue, NULL) #define AIO_QUEUE_VAL InterlockedCompareExchangePointer(&sim_asynch_queue, sim_asynch_queue, NULL)
#define AIO_QUEUE_SET(val, queue) InterlockedCompareExchangePointer(&sim_asynch_queue, val, queue) #define AIO_QUEUE_SET(val, queue) InterlockedCompareExchangePointer(&sim_asynch_queue, val, queue)
#define AIO_UPDATE_QUEUE \ #define AIO_UPDATE_QUEUE \
@ -695,7 +704,7 @@ extern int32 sim_asynch_inst_latency;
do \ do \
q = AIO_QUEUE_VAL; \ q = AIO_QUEUE_VAL; \
while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q)); \ while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q)); \
sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "found asynch event for %s after %d instructions\n", sim_uname(q), q->a_event_time);\ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Found Asynch event for %s after %d instructions\n", sim_uname(q), q->a_event_time);\
while (q != QUEUE_LIST_END) { /* List !Empty */ \ while (q != QUEUE_LIST_END) { /* List !Empty */ \
uptr = q; \ uptr = q; \
q = q->a_next; \ q = q->a_next; \
@ -715,7 +724,7 @@ extern int32 sim_asynch_inst_latency;
#define AIO_ACTIVATE(caller, uptr, event_time) \ #define AIO_ACTIVATE(caller, uptr, event_time) \
if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \
UNIT *ouptr = (uptr); \ UNIT *ouptr = (uptr); \
sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "queueing asynch event for %s after %d instructions\n", sim_uname(ouptr), event_time);\ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(ouptr), event_time);\
if (ouptr->a_next) { \ if (ouptr->a_next) { \
ouptr->a_activate_call = sim_activate_abs; \ ouptr->a_activate_call = sim_activate_abs; \
} else { \ } else { \
@ -740,13 +749,14 @@ extern int32 sim_asynch_inst_latency;
return SCPE_OK; \ return SCPE_OK; \
} else (void)0 } else (void)0
#else /* !USE_AIO_INTRINSICS */ #else /* !USE_AIO_INTRINSICS */
#define AIO_QUEUE_MODE "Lock based asynchronous event Queue access"
/* This approach uses a pthread mutex to manage access to the link list */ /* This approach uses a pthread mutex to manage access to the link list */
/* head sim_asynch_queue. It will always work, but may be slower than the */ /* head sim_asynch_queue. It will always work, but may be slower than the */
/* lock free approach when using USE_AIO_INTRINSICS */ /* lock free approach when using USE_AIO_INTRINSICS */
#define AIO_UPDATE_QUEUE \ #define AIO_UPDATE_QUEUE \
if (1) { \ if (1) { \
UNIT *uptr; \ UNIT *uptr; \
pthread_mutex_lock (&sim_asynch_lock); \ AIO_LOCK; \
while (sim_asynch_queue != QUEUE_LIST_END) { /* List !Empty */ \ while (sim_asynch_queue != QUEUE_LIST_END) { /* List !Empty */ \
int32 a_event_time; \ int32 a_event_time; \
uptr = sim_asynch_queue; \ uptr = sim_asynch_queue; \
@ -762,12 +772,13 @@ extern int32 sim_asynch_inst_latency;
a_event_time = uptr->a_event_time; \ a_event_time = uptr->a_event_time; \
uptr->a_activate_call (uptr, a_event_time); \ uptr->a_activate_call (uptr, a_event_time); \
if (uptr->a_check_completion) { \ if (uptr->a_check_completion) { \
sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "calling completion check for asynch event on %s\n", sim_uname(uptr));\
pthread_mutex_unlock (&sim_asynch_lock); \ pthread_mutex_unlock (&sim_asynch_lock); \
uptr->a_check_completion (uptr); \ uptr->a_check_completion (uptr); \
pthread_mutex_lock (&sim_asynch_lock); \ pthread_mutex_lock (&sim_asynch_lock); \
} \ } \
} \ } \
pthread_mutex_unlock (&sim_asynch_lock); \ AIO_UNLOCK; \
} else (void)0 } else (void)0
#define AIO_ACTIVATE(caller, uptr, event_time) \ #define AIO_ACTIVATE(caller, uptr, event_time) \
if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \