Untangle circular include references between tmxr serial and sock include files.

This commit is contained in:
Mark Pizzolato 2013-01-21 11:29:27 -08:00
parent 4a5b7a78c5
commit d46ebc7d49
9 changed files with 190 additions and 52 deletions

View file

@ -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.

2
scp.c
View file

@ -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

1
scp.h
View file

@ -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 */

View file

@ -119,6 +119,7 @@
#include "sim_defs.h"
#include "sim_tmxr.h"
#include "sim_serial.h"
#include "sim_timer.h"
#include <ctype.h>

View file

@ -113,6 +113,12 @@
#include <errno.h>
#include <limits.h>
#ifdef _WIN32
#include <winsock2.h>
#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 <winsock2.h>
#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)

View file

@ -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 <windows.h>
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 <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
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);

View file

@ -47,7 +47,6 @@
#define _SIM_SOCK_H_ 0
#if defined (_WIN32) /* Windows */
#undef INT_PTR /* hack, hack */
#include <winsock2.h>
#elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */

View file

@ -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:

View file

@ -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);