From d46ebc7d495753ce9a21ea419775d83d48a9db38 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 21 Jan 2013 11:29:27 -0800 Subject: [PATCH] Untangle circular include references between tmxr serial and sock include files. --- 0readmeAsynchIO.txt | 146 ++++++++++++++++++++++++++++++++++++++++++++ scp.c | 2 + scp.h | 1 + sim_console.c | 1 + sim_defs.h | 14 ++--- sim_serial.h | 49 +++++++-------- sim_sock.h | 1 - sim_tmxr.c | 13 ---- sim_tmxr.h | 15 +++-- 9 files changed, 190 insertions(+), 52 deletions(-) diff --git a/0readmeAsynchIO.txt b/0readmeAsynchIO.txt index c565e7cc..df4fa874 100644 --- a/0readmeAsynchIO.txt +++ b/0readmeAsynchIO.txt @@ -170,3 +170,149 @@ device layer (in sim_tape.c) which combined these multiple operations. This approach will dovetail well with a potential future addition of operations on physical tapes as yet another supported tape format. +Programming Console and Multiplexer devices to leverage Asynch I/O to +minimize 'unproductive' polling. + +There are two goals for asynchronous Multiplexer I/O: 1) Minimize polling +to only happen when data is available, not arbitrarily on every clock tick, +and 2) to have polling actually happen as soon as data may be available. +In most cases no effort is required to add Asynch I/O support to a +multiplexer device emulation. If a device emulation takes the normal +model of polling for arriving data on every simulated clock tick, then if +Asynch I/O is enabled, the device will operate asynchronously and behave +well. There is one restriction in this model. Specifically, the device +emulation logic can't expect that there will be a particular number (clock +tick rate maybe) of invocations of a unit service routine to perform polls +in any interval of time (this is what we're trying to change, right?). +Therefore presumptions about measuring time by counting polls is not +valid. If a device needs to manage time related activities, then the +device should create a separate unit which is dedicated to the timing +activities and which explicitly schedules a different unit service routine +for those activities as needed. Such scheduled polling should only be +enabled when actual timing is required. + +A device which is unprepared to operate asynchronously can specifically +disable multiplexer Asynch I/O for that device by explicitly defining +NO_ASYNCH_MUX at compile time. This can be defined at the top of a +particular device emulation which isn't capable of asynch operation, or +it can be defined globally on the compile command line for the simulator. +Alternatively, if a specific Multiplexer device doesn't function correctly +under the multiplexer asynchronous environment and it will never be +revised to operate correctly, it may statically set the TMUF_NOASYNCH bit +in its unit flags field. + +Some devices will need a small amount of extra coding to leverage the +Multiplexer Asynch I/O capabilties. Devices which require extra coding +have one or more of the following characteristics: +- they poll for input data on a different unit (or units) than the unit + which was provided when tmxr_attach was called. +- they poll for connections on a different unit than the unit which was + provided when tmxr_attach was called. + +The extra coding required for proper operation is to call +tmxr_set_line_unit() to associate the appropriate input polling unit to +the respective multiplexer line (ONLY if input polling is done by a unit +different than the unit specified when the MUX was attached). If output +polling is done on a different unit, then tmxr_set_line_output_unit() +should be called to describe that fact. + +Console I/O can operate asynchronously if the simulator notifies the +tmxr/console subsystem which device unit is used by the simulator to poll +for console input and output units. This is done by including sim_tmxr.h +in the source module which contains the console input device definition +and calling tmxr_set_console_units(). tmxr_set_console_units would usually +be called in a device reset routine. + +sim_tmxr consumers: + - Altair Z80 SIO devices = 1, units = 1, lines = 4, flagbits = 8, Untested Asynch + - HP2100 BACI devices = 1, units = 1, lines = 1, flagbits = 3, Untested Asynch + - HP2100 MPX devices = 1, units = 10, lines = 8, flagbits = 2, Untested Asynch + - HP2100 MUX devices = 3, units = 1/16/1, lines = 16, flagbits = 4, Untested Asynch + - I7094 COM devices = 2, units = 4/33, lines = 33, flagbits = 4, Untested Asynch + - Interdata PAS devices = 2, units = 1/32, lines = 32, flagbits = 3, Untested Asynch + - Nova QTY devices = 1, units = 1, lines = 64, flagbits = 1, Untested Asynch + - Nova TT1 devices = 2, units = 1/1, lines = 1, flagbits = 1, Untested Asynch + - PDP-1 DCS devices = 2, units = 1/32, lines = 32, flagbits = 0, Untested Asynch + - PDP-8 TTX devices = 2, units = 1/4, lines = 4, flagbits = 0, Untested Asynch + - PDP-11 DC devices = 2, units = 1/16, lines = 16, flagbits = 5, Untested Asynch + - PDP-11 DL devices = 2, units = 1/16, lines = 16, flagbits = 3, Untested Asynch + - PDP-11 DZ devices = 1, units = 1/1, lines = 32, flagbits = 0, Good Asynch + - PDP-11 VH devices = 1, units = 4, lines = 32, flagbits = 4, Good Asynch + - PDP-18b TT1 devices = 2, units = 1/16, lines = 16, flagbits = 0, Untested Asynch + - SDS MUX devices = 2, units = 1/32, lines = 32, flagbits = 0, Untested Asynch + - sim_console Good Asynch + +Program Clock Devices to leverage Asynsh I/O + +simh's concept of time is calibrated by counting the number of +instructions which the simulator can execute in a given amount of wall +clock time. Once this is determined, the appropriate value is continually +recalibrated and used throughout a simulator to schedule device time +related delays as needed. Historically, this was fine until modern +processors started having dynamically variable processor clock rates. +On such host systems, the simulator's concept of time passing can vary +drastically. This dynamic adjustment of the host system's execution rate +may cause dramatic drifting of the simulated operating system's concept +of time. Once all devices are disconnected from the calibrated clock's +instruction count, the only concern for time in the simulated system is +that it's clock tick be as accurate as possible. This has worked well +in the past, however each simulator was burdened with providing code +which facilitated managing the concept of the relationship between the +number of instructions executed and the passage of wall clock time. +To accomodate the needs of activities or events which should be measured +against wall clock time (vs specific number of instructions executed), +the simulator framework has been extended to specifically provide event +scheduling based on elapsed wall time. A new API can be used by devices +to schedule unit event delivery after the passage of a specific amount +of wall clock time. The api sim_activate_after() provides this +capability. This capability is not limited to being available ONLY when +compiling with SIM_SYNCH_IO defined. When SIM_ASYNCH_IO is defined, this +facility is implemented by a thread which drives the delivery of these +events from the host system's clock ticks (interpolated as needed to +accomodate hosts with relatively large clock ticks). When SIM_ASYNCH_IO +is not defined, this facility is implemented using the traditional simh +calibrated clock approach. This new approach has been measured to provide +clocks which drift far less than the drift realized in prior simh versions. +Using the released simh v3.9-0 vax simulator with idling enabled, the clock +drifted some 4 minutes in 35 minutes time (approximately 10%). The same OS +disk also running with idling enabled booted for 4 hours had less that 5 +seconds of clock drift (approximately 0.03%). + +Co-Scheduling Clock and Multiplexer (or other devices) + +Many simulator devices have needs to periodically executed with timing on the +order of the simulated system's clock ticks. There are numerous reasons for +this type of execution. Meanwhile, many of these events aren't particular +about exactly when they execute as long as they execute frequently enough. +Frequently executing events has the potential to interfere with a simulator's +attempts to idle when the simulated system isn't actually doing useful work. + +Interactions with attempts to 'co-schedule' multiplexer polling with clock +ticks can cause strange simulator behaviors. These strange behaviors only +happen under a combination of conditions: + 1) a multiplexer device is defined in the simulator configuration, + 2) the multiplexor device is NOT attached, and thus is not being managed by + the asynchronous multiplexer support + 3) the multiplexer device schedules polling (co-scheduled) when not + attached (such polling will never produce any input, so this is probably + a bug). +In prior simh versions support for clock co-scheduling was implmented +separately by each simulator, and usually was expressed by code of the form: + sim_activate (uptr, clk_cosched (tmxr_poll)); +As a part of asynchronous timer support, the simulator framework has been +extended to generically provide clock co-scheduling support. The use of this +new capability requires an initial call (usually in the clock device reset +routing) of the form: + sim_register_clock_unit (&clk_unit); +Once the clock unit has been registered, co-scheduling is achieved by replacing +the earlier sim_activate with the following: + sim_clock_coschedule (&dz_unit, tmxr_poll); + +Run time requirements to use SIM_ASYNCH_IO. +The Posix threads API (pthreads) is required for asynchronous execution. +Most *nix platforms have these APIs available and on these platforms +simh is typically built with these available since on these platforms, +pthreads is required for simh networking support. Windows can also +utilize the pthreads APIs if the compile and run time support for the +win32Pthreads package has been installed on the build system. + diff --git a/scp.c b/scp.c index 6ba4544b..d23469a2 100644 --- a/scp.c +++ b/scp.c @@ -328,6 +328,8 @@ t_bool sim_asynch_enabled = TRUE; int32 sim_asynch_check; int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */ +#else +t_bool sim_asynch_enabled = FALSE; #endif /* The per-simulator init routine is a weak global that defaults to NULL diff --git a/scp.h b/scp.h index 56f6e184..6eee016e 100644 --- a/scp.h +++ b/scp.h @@ -162,6 +162,7 @@ extern volatile int32 stop_cpu; extern uint32 sim_brk_types; /* breakpoint info */ extern uint32 sim_brk_dflt; extern uint32 sim_brk_summ; +extern t_bool sim_asynch_enabled; /* VM interface */ diff --git a/sim_console.c b/sim_console.c index 80211b01..60434b76 100644 --- a/sim_console.c +++ b/sim_console.c @@ -119,6 +119,7 @@ #include "sim_defs.h" #include "sim_tmxr.h" +#include "sim_serial.h" #include "sim_timer.h" #include diff --git a/sim_defs.h b/sim_defs.h index f31ec44b..14559077 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -113,6 +113,12 @@ #include #include +#ifdef _WIN32 +#include +#undef PACKED /* avoid macro name collision */ +#undef ERROR /* avoid macro name collision */ +#endif + #ifndef TRUE #define TRUE 1 #define FALSE 0 @@ -662,7 +668,6 @@ extern UNIT * volatile sim_wallclock_queue; extern UNIT * volatile sim_wallclock_entry; extern UNIT * volatile sim_clock_cosched_queue; extern volatile t_bool sim_idle_wait; -extern t_bool sim_asynch_enabled; extern int32 sim_asynch_check; extern int32 sim_asynch_latency; extern int32 sim_asynch_inst_latency; @@ -846,13 +851,6 @@ extern int32 sim_asynch_inst_latency; else \ (void)0 #ifdef _WIN32 -#include -#ifdef PACKED -#undef PACKED -#endif /* PACKED */ -#ifdef ERROR -#undef ERROR -#endif /* ERROR */ #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) #define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) __sync_val_compare_and_swap(Destination, Comparand, Exchange) #elif defined(__DECC_VER) diff --git a/sim_serial.h b/sim_serial.h index f632e973..9b1d2a7c 100644 --- a/sim_serial.h +++ b/sim_serial.h @@ -30,9 +30,7 @@ #ifndef _SIM_SERIAL_H_ #define _SIM_SERIAL_H_ 0 -#if defined (_WIN32) - -/* Windows definitions */ +#if defined (_WIN32) /* Windows definitions */ /* We need the basic Win32 definitions, but including "windows.h" also includes "winsock.h" as well. However, "sim_sock.h" explicitly includes "winsock2.h," @@ -43,50 +41,49 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif - #include - -typedef HANDLE SERHANDLE; - +#if !defined(INVALID_HANDLE) #define INVALID_HANDLE INVALID_HANDLE_VALUE +#endif /* !defined(INVALID_HANDLE) */ - -#elif defined (__unix__) || defined(__APPLE__) - -/* UNIX definitions */ +#elif defined (__unix__) || defined(__APPLE__) /* UNIX definitions */ #include #include #include #include -typedef int SERHANDLE; - +#if !defined(INVALID_HANDLE) #define INVALID_HANDLE -1 +#endif /* !defined(INVALID_HANDLE) */ - -#elif defined (VMS) - -/* VMS definitions */ - -typedef int SERHANDLE; - +#elif defined (VMS) /* VMS definitions */ +#if !defined(INVALID_HANDLE) #define INVALID_HANDLE (uint32)(-1) +#endif /* !defined(INVALID_HANDLE) */ -#else - -/* Non-implemented definitions */ - -typedef int SERHANDLE; +#else /* Non-implemented definitions */ +#if !defined(INVALID_HANDLE) #define INVALID_HANDLE -1 +#endif /* !defined(INVALID_HANDLE) */ +#endif /* OS variants */ + +#ifndef _SERHANDLE_DEFINED +#define _SERHANDLE_DEFINED 0 +#if defined (_WIN32) /* Windows definitions */ +typedef void *SERHANDLE; +#else /* all other platforms */ +typedef int SERHANDLE; #endif +#endif /* _SERHANDLE_DEFINED */ + /* Common definitions */ /* Global routines */ -#include "sim_tmxr.h" /* need TMLN definition and modem definitions */ +#include "sim_tmxr.h" /* need TMLN definition and modem definitions */ extern SERHANDLE sim_open_serial (char *name, TMLN *lp, t_stat *status); extern t_stat sim_config_serial (SERHANDLE port, const char *config); diff --git a/sim_sock.h b/sim_sock.h index 039ed019..36d4122c 100644 --- a/sim_sock.h +++ b/sim_sock.h @@ -47,7 +47,6 @@ #define _SIM_SOCK_H_ 0 #if defined (_WIN32) /* Windows */ -#undef INT_PTR /* hack, hack */ #include #elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */ diff --git a/sim_tmxr.c b/sim_tmxr.c index 8f5a3edf..dda4c17f 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -1853,19 +1853,6 @@ mp->ldsc[line].uptr = uptr_poll; return SCPE_OK; } -t_stat tmxr_set_console_input_unit (UNIT *uptr) -{ -extern TMLN sim_con_ldsc; - -sim_con_ldsc.uptr = uptr; -if (!(uptr->dynflags & UNIT_TM_POLL)) { - uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ - } -else - sim_cancel (uptr); -return SCPE_OK; -} - /* Declare which unit polls for output Inputs: diff --git a/sim_tmxr.h b/sim_tmxr.h index f94f2550..bdf786a5 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -44,12 +44,19 @@ added tmxr_rqln, tmxr_tqln */ -#include "sim_serial.h" /* We need serial I/O (SERHANDLE) */ - #ifndef _SIM_TMXR_H_ #define _SIM_TMXR_H_ 0 -#include "sim_sock.h" /* We need sockets */ +#ifndef _SERHANDLE_DEFINED +#define _SERHANDLE_DEFINED 0 +#if defined (_WIN32) /* Windows definitions */ +typedef void *SERHANDLE; +#else /* all other platforms */ +typedef int SERHANDLE; +#endif +#endif + +#include "sim_sock.h" #define TMXR_V_VALID 15 #define TMXR_VALID (1 << TMXR_V_VALID) @@ -159,7 +166,7 @@ t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear t_stat tmxr_set_config_line (TMLN *lp, char *config); t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll); -t_stat tmxr_set_console_input_unit (UNIT *uptr); +t_stat tmxr_set_console_units (UNIT *rxuptr, UNIT *txuptr); t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); void tmxr_msg (SOCKET sock, char *msg);