From 6cf54e83414c1cf0e58fd8c423ec7de001aa0220 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Tue, 7 May 2013 11:22:29 -0700 Subject: [PATCH] Fixes for stable operation with SIM_ASYNCH_CLOCKS defined --- VAX/vax_sysdev.c | 4 +- scp.c | 28 +++++++--- sim_console.c | 2 + sim_defs.h | 140 ++++++++++++++++++++++++++++++++++++++--------- sim_timer.c | 40 +++++++++----- sim_timer.h | 1 + 6 files changed, 167 insertions(+), 48 deletions(-) diff --git a/VAX/vax_sysdev.c b/VAX/vax_sysdev.c index dda6a00b..98dbdbef 100644 --- a/VAX/vax_sysdev.c +++ b/VAX/vax_sysdev.c @@ -1580,8 +1580,10 @@ if ((tmr_inc[tmr] == TMR_INC) && (tmr_time > clk_time)) { tmr_inc[tmr] = (uint32) (((double) clk_time * TMR_INC) / tmr_poll); tmr_time = clk_time; + sim_clock_coschedule (&sysd_unit[tmr], tmr_time); } -sim_activate (&sysd_unit[tmr], tmr_time); +else + sim_activate (&sysd_unit[tmr], tmr_time); return; } diff --git a/scp.c b/scp.c index f76cbe04..9d49196c 100644 --- a/scp.c +++ b/scp.c @@ -2934,8 +2934,8 @@ else { } } if (sim_clock_cosched_queue != QUEUE_LIST_END) { - fprintf (st, "%s clock co-schedule event queue status, time = %.0f\n", - sim_name, sim_time); + fprintf (st, "%s clock (%s) co-schedule event queue status, time = %.0f\n", + sim_name, sim_uname(sim_clock_unit), sim_time); for (uptr = sim_clock_cosched_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); @@ -3934,6 +3934,8 @@ char *sim_uname (UNIT *uptr) DEVICE *d = find_dev_from_unit(uptr); static AIO_TLS char uname[CBUFSIZE]; +if (!d) + return ""; if (d->numunits == 1) return sim_dname (d); sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units)); @@ -6336,28 +6338,39 @@ AIO_CANCEL(uptr); AIO_UPDATE_QUEUE; if (sim_clock_queue == QUEUE_LIST_END) return SCPE_OK; +sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr)); UPDATE_SIM_TIME; /* update sim time */ if (!sim_is_active (uptr)) return SCPE_OK; nptr = QUEUE_LIST_END; -if (sim_clock_queue == uptr) +if (sim_clock_queue == uptr) { nptr = sim_clock_queue = uptr->next; + uptr->next = NULL; /* hygiene */ + } else { for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) { if (cptr->next == uptr) { nptr = cptr->next = uptr->next; + uptr->next = NULL; /* hygiene */ break; /* end queue scan */ } } } if (nptr != QUEUE_LIST_END) - nptr->time = nptr->time + uptr->time; -uptr->next = NULL; /* hygiene */ -uptr->time = 0; + nptr->time += (uptr->next) ? 0 : uptr->time; +if (!uptr->next) + uptr->time = 0; if (sim_clock_queue != QUEUE_LIST_END) sim_interval = sim_clock_queue->time; else sim_interval = noqueue_time = NOQUEUE_WAIT; +if (sim_is_active(uptr)) { + if (sim_deb) { + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Cancel failed for %s\n", sim_uname(uptr)); + fclose(sim_deb); + } + abort (); + } return SCPE_OK; } @@ -6413,7 +6426,8 @@ return 0; double sim_gtime (void) { -UPDATE_SIM_TIME; +if (AIO_MAIN_THREAD) + UPDATE_SIM_TIME; return sim_time; } diff --git a/sim_console.c b/sim_console.c index d5ceb23e..c0bc8d43 100644 --- a/sim_console.c +++ b/sim_console.c @@ -541,6 +541,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); c = c & ~TMXR_VALID; if (c != sim_int_char) continue; /* ^E (the interrupt character) must start console interaction */ + sim_stop_timer_services (); for (j=0; j < sim_rem_con_tmxr.lines; j++) { lp = &sim_rem_con_tmxr.ldsc[j]; if ((i == j) || (!lp->conn)) @@ -727,6 +728,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); tmxr_linemsg (lp, "Simulator Running..."); tmxr_send_buffered_data (lp); } + sim_start_timer_services (); break; } if (cmdp && (cmdp->action == &x_step_cmd)) { diff --git a/sim_defs.h b/sim_defs.h index ca63e5f5..6e66bbb2 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -749,7 +749,24 @@ extern int32 sim_asynch_inst_latency; /* It is primarily used only used in debugging messages */ #define AIO_TLS #endif - +#define AIO_QUEUE_CHECK(que, lock) \ + if (1) { \ + UNIT *_cptr; \ + if (lock) \ + pthread_mutex_lock (lock); \ + for (_cptr = que; \ + (_cptr != QUEUE_LIST_END); \ + _cptr = _cptr->next) \ + if (!_cptr->next) { \ + if (sim_deb) { \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Corruption detected\n");\ + fclose(sim_deb); \ + } \ + abort(); \ + } \ + if (lock) \ + pthread_mutex_unlock (lock); \ + } else (void)0 #define AIO_MAIN_THREAD (pthread_equal ( pthread_self(), sim_asynch_main_threadid )) #define AIO_LOCK \ pthread_mutex_lock(&sim_asynch_lock) @@ -768,14 +785,16 @@ extern int32 sim_asynch_inst_latency; if ((uptr)->a_cancel) \ (uptr)->a_cancel (uptr); \ else { \ - if (AIO_IS_ACTIVE (uptr)) { \ - UNIT *cptr, *nptr; \ + if ((uptr)->next) { \ + UNIT *cptr; \ AIO_UPDATE_QUEUE; \ pthread_mutex_lock (&sim_timer_lock); \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_wallclock_queue) { \ sim_wallclock_queue = (uptr)->next; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ + sim_timer_event_canceled = TRUE; \ + pthread_cond_signal (&sim_timer_wake); \ } \ else \ for (cptr = sim_wallclock_queue; \ @@ -783,18 +802,13 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ break; \ } \ - if (nptr == QUEUE_LIST_END) { \ - sim_timer_event_canceled = TRUE; \ - pthread_cond_signal (&sim_timer_wake); \ - } \ if ((uptr)->next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ else { \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_clock_cosched_queue) { \ sim_clock_cosched_queue = (uptr)->next; \ (uptr)->next = NULL; \ @@ -805,10 +819,18 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ break; \ } \ + if ((uptr)->next == NULL) { \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Clock Coscheduling Event for %s\n", sim_uname(uptr));\ + } \ + } \ + while (sim_timer_event_canceled) { \ + pthread_mutex_unlock (&sim_timer_lock); \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Waiting for Timer Event cancelation for %s\n", sim_uname(uptr));\ + sim_os_ms_sleep (0); \ + pthread_mutex_lock (&sim_timer_lock); \ } \ pthread_mutex_unlock (&sim_timer_lock); \ } \ @@ -838,14 +860,16 @@ extern int32 sim_asynch_inst_latency; sim_tmxr_poll_count -= (uptr)->a_poll_waiter_count; \ (uptr)->a_poll_waiter_count = 0; \ } \ - if (AIO_IS_ACTIVE (uptr)) { \ - UNIT *cptr, *nptr; \ + if ((uptr)->next) { \ + UNIT *cptr; \ AIO_UPDATE_QUEUE; \ pthread_mutex_lock (&sim_timer_lock); \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_wallclock_queue) { \ sim_wallclock_queue = (uptr)->next; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ + sim_timer_event_canceled = TRUE; \ + pthread_cond_signal (&sim_timer_wake); \ } \ else \ for (cptr = sim_wallclock_queue; \ @@ -853,18 +877,13 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ break; \ } \ - if (nptr == QUEUE_LIST_END) { \ - sim_timer_event_canceled = TRUE; \ - pthread_cond_signal (&sim_timer_wake); \ - } \ if ((uptr)->next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ else { \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_clock_cosched_queue) { \ sim_clock_cosched_queue = (uptr)->next; \ (uptr)->next = NULL; \ @@ -875,10 +894,18 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ break; \ } \ + if ((uptr)->next == NULL) { \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Clock Coscheduling Event for %s\n", sim_uname(uptr));\ + } \ + } \ + while (sim_timer_event_canceled) { \ + pthread_mutex_unlock (&sim_timer_lock); \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Waiting for Timer Event cancelation for %s\n", sim_uname(uptr));\ + sim_os_ms_sleep (0); \ + pthread_mutex_lock (&sim_timer_lock); \ } \ pthread_mutex_unlock (&sim_timer_lock); \ } \ @@ -988,8 +1015,8 @@ extern int32 sim_asynch_inst_latency; do \ q = AIO_QUEUE_VAL; \ 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);\ while (q != QUEUE_LIST_END) { /* List !Empty */ \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Migrating Asynch event for %s after %d instructions\n", sim_uname(q), q->a_event_time);\ uptr = q; \ q = q->a_next; \ uptr->a_next = NULL; /* hygiene */ \ @@ -1014,8 +1041,8 @@ extern int32 sim_asynch_inst_latency; } else { \ UNIT *q, *qe; \ ouptr->a_event_time = event_time; \ - uptr->a_activate_call = caller; \ - uptr->a_next = QUEUE_LIST_END; /* Mark as on list */ \ + ouptr->a_activate_call = caller; \ + ouptr->a_next = QUEUE_LIST_END; /* Mark as on list */ \ do { \ do \ q = AIO_QUEUE_VAL; \ @@ -1035,6 +1062,39 @@ extern int32 sim_asynch_inst_latency; } \ return SCPE_OK; \ } else (void)0 +#define AIO_ACTIVATE_LIST(caller, list, event_time) \ + if (1) { \ + UNIT *ouptr, *q, *qe; \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch events for %s after %d instructions\n", sim_uname(list), event_time);\ + for (qe=(list); qe->next != QUEUE_LIST_END;) { \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->a_next = qe->next; \ + qe->next = NULL; \ + qe = qe->a_next; \ + } \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->a_next = QUEUE_LIST_END; \ + qe->next = NULL; \ + ouptr = (list); \ + do { \ + do \ + q = AIO_QUEUE_VAL; \ + while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q));/* Grab current list */ \ + for (qe = ouptr; qe->a_next != QUEUE_LIST_END; qe = qe->a_next); \ + qe->a_next = q; /* append current list */ \ + do \ + q = AIO_QUEUE_VAL; \ + while (q != AIO_QUEUE_SET(ouptr, q)); \ + ouptr = q; \ + } while (ouptr != QUEUE_LIST_END); \ + sim_asynch_check = 0; /* try to force check */ \ + if (sim_idle_wait) { \ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(ouptr), event_time);\ + pthread_cond_signal (&sim_asynch_wake); \ + } \ + } else (void)0 #else /* !USE_AIO_INTRINSICS */ /* 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 */ @@ -1098,8 +1158,8 @@ extern int32 sim_asynch_inst_latency; } else (void)0 #define AIO_ACTIVATE(caller, uptr, event_time) \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ - sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "queueing asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\ - AIO_UNLOCK; \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\ + AIO_LOCK; \ if (uptr->a_next) { /* already queued? */ \ uptr->a_activate_call = sim_activate_abs; \ } else { \ @@ -1108,12 +1168,38 @@ extern int32 sim_asynch_inst_latency; uptr->a_activate_call = caller; \ sim_asynch_queue = uptr; \ } \ - if (sim_idle_wait) \ + if (sim_idle_wait) { \ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(uptr), event_time);\ pthread_cond_signal (&sim_asynch_wake); \ + } \ AIO_UNLOCK; \ sim_asynch_check = 0; \ return SCPE_OK; \ } else (void)0 +#define AIO_ACTIVATE_LIST(caller, list, event_time) \ + if (1) { \ + UNIT *qe; \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch events for %s after %d instructions\n", sim_uname(list), event_time);\ + for (qe=list; qe->next != QUEUE_LIST_END;) { \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->a_next = qe->next; \ + qe->next = NULL; \ + qe = qe->a_next; \ + } \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->next = NULL; \ + AIO_LOCK; \ + qe->a_next = sim_asynch_queue; \ + sim_asynch_queue = list; \ + sim_asynch_check = 0; /* try to force check */ \ + if (sim_idle_wait) { \ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(list), event_time);\ + pthread_cond_signal (&sim_asynch_wake); \ + } \ + AIO_UNLOCK; \ + } else (void)0 #endif /* USE_AIO_INTRINSICS */ #define AIO_VALIDATE if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) abort() #define AIO_CHECK_EVENT \ diff --git a/sim_timer.c b/sim_timer.c index bddf7911..f5b0f9bb 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -101,7 +101,7 @@ static uint32 sim_throt_val = 0; static uint32 sim_throt_state = 0; static uint32 sim_throt_sleep_time = 0; static int32 sim_throt_wait = 0; -static UNIT *sim_clock_unit = NULL; +UNIT *sim_clock_unit = NULL; t_bool sim_asynch_timer = #if defined (SIM_ASYNCH_CLOCKS) TRUE; @@ -1216,7 +1216,7 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { if (sim_wallclock_queue == QUEUE_LIST_END) sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting forever\n"); else - sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting for %.0f usecs until %.6f\n", wait_usec, sim_wallclock_queue->a_due_time); + sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting for %.0f usecs until %.6f for %s\n", wait_usec, sim_wallclock_queue->a_due_time, sim_uname(sim_wallclock_queue)); if ((wait_usec <= 0.0) || (0 != pthread_cond_timedwait (&sim_timer_wake, &sim_timer_lock, &due_time))) { if (sim_wallclock_queue == QUEUE_LIST_END) /* queue empty? */ @@ -1238,21 +1238,28 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { } sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - slept %.0fms - activating(%s,%d)\n", 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay); - sim_activate (uptr, inst_delay); - if (sim_clock_unit == uptr) - while (sim_clock_cosched_queue != QUEUE_LIST_END) { - uptr = sim_clock_cosched_queue; - sim_clock_cosched_queue = uptr->next; - uptr->next = NULL; - sim_activate (uptr, inst_delay); - } + if (sim_clock_unit == uptr) { + /* + * Some devices may depend on executing during the same instruction as the + * clock tick event. We link the clock coschedule queue to the clock tick + * and then insert that list in the asynch event queue in a single operation + */ + uptr->next = sim_clock_cosched_queue; + sim_clock_cosched_queue = QUEUE_LIST_END; + AIO_ACTIVATE_LIST(sim_activate, uptr, inst_delay); + } + else + sim_activate (uptr, inst_delay); + continue; } - else /* Something wants to adjust the queue */ + else /* Something wants to adjust the queue since the wait condition was signaled */ if (sim_timer_event_canceled) sim_timer_event_canceled = FALSE; /* reset flag and continue */ else - if (sim_wallclock_entry == NULL) /* nothing to insert? */ + if (sim_wallclock_entry == NULL) { /* nothing to insert? */ + sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - condition wakeup without new entry\n"); break; /* stop processing entries */ + } } pthread_mutex_unlock (&sim_timer_lock); @@ -1426,9 +1433,16 @@ if (1) { sim_uname(uptr), uptr->a_due_time); } pthread_mutex_lock (&sim_timer_lock); +while (sim_wallclock_entry) { + sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - queue insert entry %s busy waiting for 1ms\n", + sim_uname(sim_wallclock_entry)); + pthread_mutex_unlock (&sim_timer_lock); + sim_os_ms_sleep (1); + pthread_mutex_lock (&sim_timer_lock); + } sim_wallclock_entry = uptr; -pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */ pthread_mutex_unlock (&sim_timer_lock); +pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */ return SCPE_OK; #else return _sim_activate (uptr, inst_delay); /* queue it now */ diff --git a/sim_timer.h b/sim_timer.h index d5f7981c..4448567f 100644 --- a/sim_timer.h +++ b/sim_timer.h @@ -119,6 +119,7 @@ uint32 sim_timer_idle_capable (uint32 *hoat_tick_ms); extern t_bool sim_idle_enab; /* idle enabled flag */ extern volatile t_bool sim_idle_wait; /* idle waiting flag */ +extern UNIT *sim_clock_unit; extern t_bool sim_asynch_timer; extern DEVICE sim_timer_dev;