Skip to content

Commit 314192d

Browse files
committed
SCP: Fix scp_asynch_check cross-thread interference
Address a Clang/LLVM sanitizer warning that scp_asynch_check is written by both the AIO and main threads, one thread potentially clobbering the other's value. The "scp_asynch_check = 0" at scp.c:239 is where the thread sanitizer detects the issue. This assignment is supposed to cause the main thread to process I/O updates on the next AIO_CHECK_EVENT code's execution. To preserve that behavior, AIO_CHECK_EVENT now executes AIO_UPDATE_QUEUE when sim_asynch_queue != QUEUE_HEAD, i.e., there is work to be done in the queue. Code refactoring: - Convert preprocessor macro code to inline functions unless impractical. - Eliminate the asymmetry between the lock-based (mutex) and lock-free implementations. - Lock-free: AIO_ILOCK/AIO_IUNLOCK do not reacquire sim_asynch_lock when compiler intrinsics are present (GCC, Clang, MS C and DEC C on Itanium.) - Lock-based: If DONT_USE_AIO_INTRINSICS is defined, the AIO implementation becomes lock-based via mutexes and AIO_ILOCK/- AIO_IUNLOCK recursively acquire/release sim_asynch_lock. - AIO defaults to the lock-based implementation if compiler intrinsics are not available. - GCC, Clang: Prefer the __atomic intrinsics over the deprecated __sync intrinsics. The __sync intrinics still exist for older GCC compilers. - sim_asynch_lock is a recursive mutex for both lock-based and lock-free implementations. Eliminates implementation asymmetry. - AIO_CHECK_EVENT invokes AIO_ILOCK and AIO_IUNLOCK so that the lock-based code cannot alter sim_asynch_queue when checking for pending I/O work. - sim_debug_io_lock: Debug output serialization lock. sim_asynch_lock was semantically overloaded to serialize output from _sim_debug_write_flush. This lock provides better semantic clarity. - Removed sim_asynch_check. It is no longer necessary. - New builder script flag to disable AIO lock-free, force AIO lock-based code: - cmake-builder.ps1 -noaiointrinsics ... - cmake-builder.sh -no-aio-intrinics ...
1 parent c47e933 commit 314192d

9 files changed

Lines changed: 304 additions & 160 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ option(SIMH_PACKAGE_SUFFIX
229229
option(MAC_UNIVERSAL
230230
"macOS universal binary flag: TRUE -> build universal binaries, FALSE -> don't."
231231
${MAC_UNIVERSAL_OPTVAL})
232+
option(DONT_USE_AIO_INTRINSICS
233+
"Don't use compiler/platform intrinsics for AIO, revert to lock-based AIO"
234+
FALSE)
232235

233236
# Places where CMake should look for dependent package configuration fragments and artifacts:
234237
set(SIMH_PREFIX_PATH_LIST)

PDP10/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ add_simulator(pdp10-ka
9898
FEATURE_INT64
9999
FEATURE_VIDEO
100100
FEATURE_DISPLAY
101+
USES_AIO
101102
LABEL PDP10
102103
PKG_FAMILY pdp10_family
103104
TEST ka10)
@@ -139,6 +140,7 @@ add_simulator(pdp10-ki
139140
FEATURE_INT64
140141
FEATURE_VIDEO
141142
FEATURE_DISPLAY
143+
USES_AIO
142144
LABEL PDP10
143145
PKG_FAMILY pdp10_family
144146
TEST ki10)
@@ -171,6 +173,7 @@ add_simulator(pdp10-kl
171173
DEFINES
172174
KL=1
173175
FEATURE_INT64
176+
USES_AIO
174177
LABEL PDP10
175178
PKG_FAMILY pdp10_family
176179
TEST kl10)
@@ -198,6 +201,7 @@ add_simulator(pdp10-ks
198201
DEFINES
199202
KS=1
200203
FEATURE_INT64
204+
USES_AIO
201205
LABEL PDP10
202206
PKG_FAMILY pdp10_family
203207
TEST ks10)

README-CMake.md

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -511,25 +511,22 @@ or video support.
511511
512512
# List the supported command line flags:
513513
$ cmake/cmake-builder.sh --help
514-
Configure and build simh simulators on Linux and *nix-like platforms.
514+
** cmake version 3.18.4
515515
516-
Subdirectories:
517-
cmake/build-unix: Makefile-based build simulators
518-
cmake/build-ninja: Ninja build-based simulators
516+
CMake suite maintained and supported by Kitware (kitware.com/cmake).
517+
Configure and build simh simulators on Linux and *nix-like platforms.
519518
520-
Options:
521-
--------
519+
Compile/Build options:
520+
----------------------
522521
--clean (-x) Remove the build subdirectory
523522
--generate (-g) Generate the build environment, don't compile/build
524523
--parallel (-p) Enable build parallelism (parallel builds)
525-
--nonetwork Build simulators without network support
526-
--novideo Build simulators without video support
527524
--notest Do not execute 'ctest' test cases
528525
--noinstall Do not install SIMH simulators.
529526
--testonly Do not build, execute the 'ctest' test cases
530527
--installonly Do not build, install the SIMH simulators
531528

532-
--flavor (-f) Specifies the build flavor. Valid flavors are:
529+
--flavor (-f) [Required] Specifies the build flavor. Valid flavors are:
533530
unix
534531
ninja
535532
xcode
@@ -541,8 +538,7 @@ or video support.
541538
--config (-c) Specifies the build configuration: 'Release' or 'Debug'
542539

543540
--target Build a specific simulator or simulators. Separate multiple
544-
targets by separating with a comma,
545-
e.g. "--target pdp8,pdp11,vax750,altairz80,3b2"
541+
targets with a comma, e.g. "--target pdp8,pdp11,vax750,altairz80,3b2"
546542
--lto Enable Link Time Optimization (LTO) in Release builds
547543
--debugWall Enable maximal warnings in Debug builds
548544
--cppcheck Enable cppcheck static code analysis rules
@@ -553,6 +549,17 @@ or video support.
553549
554550
--verbose Turn on verbose build output
555551
552+
SIMH feature control options:
553+
-----------------------------
554+
--nonetwork Build simulators without network support
555+
--novideo Build simulators without video support
556+
--no-aio-intrinsics
557+
Do not use compiler/platform intrinsics to implement AIO
558+
functions (aka "lock-free" AIO), reverts to lock-based AIO
559+
if threading libraries are detected.
560+
561+
Other options:
562+
--------------
556563
--help (-h) Print this help.
557564
```
558565
@@ -569,17 +576,17 @@ or video support.
569576
PS C:\...\open-simh> Get-Help -deatailed cmake\cmake-builder.ps1
570577
571578
NAME
572-
C:\Users\bsm21317\play\open-simh\cmake\cmake-builder.ps1
579+
C:\...\play\open-simh\cmake\cmake-builder.ps1
573580
574581
SYNOPSIS
575582
Configure and build SIMH's dependencies and simulators using the Microsoft Visual
576583
Studio C compiler or MinGW-W64-based gcc compiler.
577584

578585

579586
SYNTAX
580-
C:\Users\bsm21317\play\open-simh\cmake\cmake-builder.ps1 [[-flavor] <String>] [[-config] <String>] [[-cpack_suffix] <String>] [[-target] <String>]
581-
[-clean] [-help] [-nonetwork] [-novideo] [-notest] [-noinstall] [-parallel] [-generate] [-regenerate] [-testonly] [-installOnly] [-windeprecation]
582-
[-package] [-lto] [-debugWall] [-cppcheck] [<CommonParameters>]
587+
C:\...\play\open-simh\cmake\cmake-builder.ps1 [[-flavor] <String>] [[-config] <String>] [[-cpack_suffix] <String>] [[-target]
588+
<String[]>] [-clean] [-help] [-nonetwork] [-novideo] [-noaioinstrinsics] [-notest] [-noinstall] [-parallel] [-generate] [-testonly]
589+
[-installOnly] [-windeprecation] [-lto] [-debugWall] [-cppcheck] [<CommonParameters>]
583590

584591

585592
DESCRIPTION
@@ -588,9 +595,9 @@ or video support.
588595

589596
1. Configure and generate the build environment selected by '-flavor' option.
590597
2. Build missing runtime dependencies and the simulator suite with the compiler
591-
configuration selected by the '-config' option. The "Release" configuration
592-
generates optimized executables; the "Debug" configuration generates
593-
development executables with debugger information.
598+
configuration selected by the '-config' option. The "Release" configuration
599+
generates optimized executables; the "Debug" configuration generates
600+
development executables with debugger information.
594601
3. Test the simulators
595602

596603
There is an install phase that can be invoked separately as part of the SIMH
@@ -624,6 +631,9 @@ or video support.
624631
mingw-make MinGW GCC/mingw32-make
625632
mingw-ninja MinGW GCC/ninja
626633
634+
-config <String>
635+
The target build configuration. Valid values: "Release" and "Debug"
636+
627637
[...truncated for brevity...]
628638
```
629639

cmake/cmake-builder.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ showHelp()
77
cat <<EOF
88
Configure and build simh simulators on Linux and *nix-like platforms.
99
10-
-Compile/Build options:
11-
-----------------------
10+
Compile/Build options:
1211
--clean (-x) Remove the build subdirectory
1312
--generate (-g) Generate the build environment, don't compile/build
1413
--cache '--generate' and show CMake's variable cache

cmake/pthreads-dep.cmake

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ if (WITH_ASYNC)
7070
else ()
7171
set(AIO_FLAGS DONT_USE_READER_THREAD)
7272
endif ()
73-
else()
73+
74+
if (DONT_USE_AIO_INTRINSICS)
75+
target_compile_definitions(thread_lib INTERFACE DONT_USE_AIO_INTRINSICS)
76+
endif ()
77+
else ()
7478
target_compile_definitions(thread_lib INTERFACE DONT_USE_READER_THREAD)
7579
set(THREADING_PKG_STATUS "asynchronous I/O disabled.")
7680
endif()

makefile

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,7 +2071,8 @@ KA10 = ${KA10D}/kx10_cpu.c ${KA10D}/kx10_sys.c ${KA10D}/kx10_df.c \
20712071
${KA10D}/ka10_ai.c ${KA10D}/ka10_iii.c ${KA10D}/kx10_disk.c \
20722072
${KA10D}/ka10_pclk.c ${KA10D}/ka10_tv.c \
20732073
${DISPLAYL} ${DISPLAY340} ${DISPLAYIII}
2074-
KA10_OPT = -DKA=1 -DUSE_INT64 -I ${KA10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KA10_DISPLAY_OPT}
2074+
KA10_OPT = -DKA=1 -DUSE_INT64 -I ${KA10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KA10_DISPLAY_OPT} \
2075+
${AIO_CCDEFS}
20752076
ifneq (${PANDA_LIGHTS},)
20762077
# ONLY for Panda display.
20772078
KA10_OPT += -DPANDA_LIGHTS
@@ -2091,7 +2092,8 @@ KI10 = ${KI10D}/kx10_cpu.c ${KI10D}/kx10_sys.c ${KI10D}/kx10_df.c \
20912092
${KI10D}/kx10_cp.c ${KI10D}/kx10_tu.c ${KI10D}/kx10_rs.c \
20922093
${KI10D}/kx10_imp.c ${KI10D}/kx10_dpy.c ${KI10D}/kx10_disk.c \
20932094
${DISPLAYL} ${DISPLAY340}
2094-
KI10_OPT = -DKI=1 -DUSE_INT64 -I ${KI10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KI10_DISPLAY_OPT}
2095+
KI10_OPT = -DKI=1 -DUSE_INT64 -I ${KI10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KI10_DISPLAY_OPT} \
2096+
${AIO_CCDEFS}
20952097
ifneq (${PANDA_LIGHTS},)
20962098
# ONLY for Panda display.
20972099
KI10_OPT += -DPANDA_LIGHTS
@@ -2107,15 +2109,15 @@ KL10 = ${KL10D}/kx10_cpu.c ${KL10D}/kx10_sys.c ${KL10D}/kx10_df.c \
21072109
${KL10D}/kx10_rp.c ${KL10D}/kx10_tu.c ${KL10D}/kx10_rs.c \
21082110
${KL10D}/kx10_imp.c ${KL10D}/kl10_fe.c ${KL10D}/ka10_pd.c \
21092111
${KL10D}/ka10_ch10.c ${KL10D}/kl10_nia.c ${KL10D}/kx10_disk.c
2110-
KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) -DUSE_SIM_CARD ${NETWORK_OPT}
2112+
KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) -DUSE_SIM_CARD ${NETWORK_OPT} ${AIO_CCDEFS}
21112113

21122114
KS10D = ${SIMHD}/PDP10
21132115
KS10 = ${KS10D}/kx10_cpu.c ${KS10D}/kx10_sys.c ${KS10D}/kx10_disk.c \
21142116
${KS10D}/ks10_cty.c ${KS10D}/ks10_uba.c ${KS10D}/kx10_rh.c \
21152117
${KS10D}/kx10_rp.c ${KS10D}/kx10_tu.c ${KS10D}/ks10_dz.c \
21162118
${KS10D}/ks10_tcu.c ${KS10D}/ks10_lp.c ${KS10D}/ks10_ch11.c \
21172119
${KS10D}/ks10_kmc.c ${KS10D}/ks10_dup.c ${KS10D}/kx10_imp.c
2118-
KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) -I $(PDP11D) ${NETWORK_OPT}
2120+
KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) -I $(PDP11D) ${NETWORK_OPT} ${AIO_CCDEFS}
21192121

21202122
ATT3B2D = ${SIMHD}/3B2
21212123
ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \

scp.c

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -380,41 +380,71 @@ int32 sim_tmxr_poll_count;
380380
pthread_t sim_asynch_main_threadid;
381381
UNIT * volatile sim_asynch_queue;
382382
t_bool sim_asynch_enabled = TRUE;
383-
int32 sim_asynch_check;
384383
int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */
385384
int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */
386385

386+
/* Debug flush mutex to serialize debug output. */
387+
pthread_mutex_t sim_debug_io_lock = PTHREAD_MUTEX_INITIALIZER;
388+
389+
/* aio_queue_worklist: Grab the current queue and replace sim_asynch_queue with
390+
* the empty queue.
391+
*
392+
* Returns the UNIT worklist to which sim_asynch_queue previously pointed.
393+
*/
394+
static SIM_INLINE volatile UNIT *aio_queue_worklist()
395+
{
396+
volatile UNIT *q;
397+
398+
do {
399+
/* Atomically load, wash-rinse-repeat if there is thread interference */
400+
q = sim_sync_load_pointer((void * volatile *) &sim_asynch_queue);
401+
} while (!sim_sync_cmpxchg((void * volatile *) &sim_asynch_queue, QUEUE_LIST_END, (void *) q));
402+
403+
return q;
404+
}
405+
406+
/* aio_enqueue_unit: Atomically add a UNIT to the sim_asynch_queue list.
407+
*/
408+
static SIM_INLINE void aio_enqueue_unit(UNIT *unit)
409+
{
410+
volatile UNIT *q;
411+
412+
do {
413+
q = sim_sync_load_pointer((void * volatile *) &sim_asynch_queue);
414+
unit->a_next = (UNIT *) q; /* Mark as on list */
415+
} while (!sim_sync_cmpxchg((void * volatile *) &sim_asynch_queue, unit, (void *) q));
416+
}
417+
387418
int sim_aio_update_queue (void)
388419
{
389420
int migrated = 0;
390421

391422
AIO_ILOCK;
392-
if (AIO_QUEUE_VAL != QUEUE_LIST_END) { /* List !Empty */
393-
UNIT *q, *uptr;
423+
if (!AIO_QUEUE_EMPTY()) {
424+
volatile UNIT *q;
425+
UNIT *uptr;
394426
int32 a_event_time;
395-
do { /* Grab current queue */
396-
q = AIO_QUEUE_VAL;
397-
} while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q));
398-
while (q != QUEUE_LIST_END) { /* List !Empty */
399-
sim_debug (SIM_DBG_AIO_QUEUE, &sim_scp_dev, "Migrating Asynch event for %s after %d %s\n", sim_uname(q), q->a_event_time, sim_vm_interval_units);
427+
for (q = aio_queue_worklist(); q != QUEUE_LIST_END; /* empty */) {
428+
uptr = (UNIT *) q;
429+
sim_debug (SIM_DBG_AIO_QUEUE, &sim_scp_dev, "Migrating Asynch event for %s after %d %s\n",
430+
sim_uname(uptr), uptr->a_event_time, sim_vm_interval_units);
400431
++migrated;
401-
uptr = q;
402432
q = q->a_next;
403-
uptr->a_next = NULL; /* hygiene */
433+
uptr->a_next = NULL; /* hygiene */
404434
if (uptr->a_activate_call != &sim_activate_notbefore) {
405-
a_event_time = uptr->a_event_time-((sim_asynch_inst_latency+1)/2);
435+
a_event_time = uptr->a_event_time - ((sim_asynch_inst_latency + 1) / 2);
406436
if (a_event_time < 0)
407437
a_event_time = 0;
408438
}
409439
else
410440
a_event_time = uptr->a_event_time;
411-
AIO_IUNLOCK;
441+
412442
uptr->a_activate_call (uptr, a_event_time);
443+
413444
if (uptr->a_check_completion) {
414445
sim_debug (SIM_DBG_AIO_QUEUE, &sim_scp_dev, "Calling Completion Check for asynch event on %s\n", sim_uname(uptr));
415446
uptr->a_check_completion (uptr);
416447
}
417-
AIO_ILOCK;
418448
}
419449
}
420450
AIO_IUNLOCK;
@@ -423,22 +453,19 @@ return migrated;
423453

424454
void sim_aio_activate (ACTIVATE_API caller, UNIT *uptr, int32 event_time)
425455
{
426-
AIO_ILOCK;
427456
sim_debug (SIM_DBG_AIO_QUEUE, &sim_scp_dev, "Queueing Asynch event for %s after %d %s\n", sim_uname(uptr), event_time, sim_vm_interval_units);
428-
if (uptr->a_next) {
457+
458+
AIO_ILOCK;
459+
if (NULL != uptr->a_next) {
429460
uptr->a_activate_call = sim_activate_abs;
430461
}
431462
else {
432-
UNIT *q;
433463
uptr->a_event_time = event_time;
434464
uptr->a_activate_call = caller;
435-
do {
436-
q = AIO_QUEUE_VAL;
437-
uptr->a_next = q; /* Mark as on list */
438-
} while (q != AIO_QUEUE_SET(uptr, q));
465+
aio_enqueue_unit(uptr);
439466
}
440467
AIO_IUNLOCK;
441-
sim_asynch_check = 0; /* try to force check */
468+
442469
if (sim_idle_wait) {
443470
sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d %s\n", sim_uname(uptr), event_time, sim_vm_interval_units);
444471
pthread_cond_signal (&sim_asynch_wake);
@@ -7036,7 +7063,7 @@ sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
70367063
pthread_mutex_lock (&sim_asynch_lock);
70377064
sim_mfile = &buf;
70387065
fprintf (st, "asynchronous pending event queue\n");
7039-
if (sim_asynch_queue == QUEUE_LIST_END)
7066+
if (AIO_QUEUE_EMPTY())
70407067
fprintf (st, " Empty\n");
70417068
else {
70427069
for (uptr = sim_asynch_queue; uptr != QUEUE_LIST_END; uptr = uptr->a_next) {
@@ -13653,7 +13680,8 @@ if (sim_deb_switches & SWMASK ('F')) { /* filtering disabled? */
1365313680
_debug_fwrite (buf, len); /* output now. */
1365413681
return; /* done */
1365513682
}
13656-
AIO_LOCK;
13683+
13684+
AIO_DEBUG_IO_ACTIVE;
1365713685
if (debug_line_offset + len + 1 > debug_line_bufsize) {
1365813686
/* realloc(NULL, size) == malloc(size). Initialize the malloc()-ed space. Only
1365913687
need to test debug_line_buf since SIMH allocates both buffers at the same
@@ -13738,7 +13766,7 @@ while (NULL != (eol = strchr (debug_line_buf, '\n')) || flush) {
1373813766
memmove (debug_line_buf, eol + 1, debug_line_offset);
1373913767
debug_line_buf[debug_line_offset] = '\0';
1374013768
}
13741-
AIO_UNLOCK;
13769+
AIO_DEBUG_IO_DONE;
1374213770
}
1374313771

1374413772
static void _sim_debug_write (const char *buf, size_t len)

0 commit comments

Comments
 (0)