Skip to content

Commit 117a996

Browse files
committed
SCP: Fix scp_asynch_check cross-thread interference
Fix 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. "scp_asynch_check = 0" 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 either sim_asynch_check decrements below 0 or there is work to be done on the AIO queue (sim_asynch_queue != QUEUE_HEAD.) Code refactoring: - AIO_ILOCK/AIO_IUNLOCK: As documented in sim_defs.h, these two macros are empty in the lock-free code case. Minor performance improvement. - AIO_QUEUE_VAL and AIO_QUEUE_SET are inlined static functions and directly use compiler intrinsics for the lock-free implementation, when available. - GCC/Clang: Use the __atomic intrinsics. __sync instrinsics have been deprecated since the C++11 standard's publication. The __sync intrinsics are still available and provide a fallback if GCC or Clang is really so old that the __atomic intrinsics are unavailable (insert Wallace Shawn's iconic "That's totally inconceivable!" here.) - AIO_QUEUE_VAL: Leverages the processor's read fence when available. Ensures that sim_asynch_queue's value is consistenly read across cores and threads in the lock-free implementation for platforms without total store ordering or strong consistency guarantees (i.e., anything other than Intel and SPARC.) - AIO_QUEUE_SET: Compare-exchange to set sim_asynch_queue's head. The return value is a boolean (1/0) to reflect the success (1) or failure (0) of the compare-exchange. - AIO_CHECK_EVENT invokes AIO_ILOCK and AIO_IUNLOCK so that the lock-based code cannot alter sim_asynch_queue when checking to see if there is pending I/O work to be done. sim_asynch_lock is a recursive mutex in the lock-based case, so AIO_ILOCK/AIO_IUNLOCK will succeed for the lock-based implementation. - 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 c077c22 commit 117a996

7 files changed

Lines changed: 247 additions & 58 deletions

File tree

CMakeLists.txt

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

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

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ param (
115115
[Parameter(Mandatory=$false)]
116116
[switch] $novideo = $false,
117117

118+
## Compile the SIMH simulator without AIO instrinsics ("lock-free" AIO),
119+
## using lock-based AIO via thread mutexes instead.
120+
[Parameter(Mandatory=$false)]
121+
[switch] $noaiointrinsics = $false,
122+
118123
## Disable the build's tests.
119124
[Parameter(Mandatory=$false)]
120125
[switch] $notest = $false,
@@ -411,6 +416,10 @@ if (($scriptPhases -contains "generate") -or ($scriptPhases -contains "build"))
411416
{
412417
$generateArgs += @("-DWITH_VIDEO:Bool=Off")
413418
}
419+
if ($noaiointrinsics)
420+
{
421+
$generateArgs += @("-DDONT_USE_AIO_INTRINSICS:Bool=On")
422+
}
414423
if ($lto)
415424
{
416425
$generateArgs += @("-DRELEASE_LTO:Bool=On")

cmake/cmake-builder.sh

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,11 @@ showHelp()
77
cat <<EOF
88
Configure and build simh simulators on Linux and *nix-like platforms.
99
10-
Subdirectories:
11-
cmake/build-unix: Makefile-based build simulators
12-
cmake/build-ninja: Ninja build-based simulators
13-
14-
Options:
15-
--------
10+
Compile/Build options:
11+
----------------------
1612
--clean (-x) Remove the build subdirectory
1713
--generate (-g) Generate the build environment, don't compile/build
1814
--parallel (-p) Enable build parallelism (parallel builds)
19-
--nonetwork Build simulators without network support
20-
--novideo Build simulators without video support
2115
--notest Do not execute 'ctest' test cases
2216
--noinstall Do not install SIMH simulators.
2317
--testonly Do not build, execute the 'ctest' test cases
@@ -46,6 +40,17 @@ Options:
4640
4741
--verbose Turn on verbose build output
4842
43+
SIMH feature control options:
44+
-----------------------------
45+
--nonetwork Build simulators without network support
46+
--novideo Build simulators without video support
47+
--no-aio-intrinsics
48+
Do not use compiler/platform intrinsics to implement AIO
49+
functions (aka "lock-free" AIO), reverts to lock-based AIO
50+
if threading libraries are detected.
51+
52+
Other options:
53+
--------------
4954
--help (-h) Print this help.
5055
EOF
5156

@@ -152,6 +157,7 @@ fi
152157

153158
longopts=clean,help,flavor:,config:,nonetwork,novideo,notest,parallel,generate,testonly
154159
longopts=${longopts},noinstall,installonly,verbose,target:,lto,debugWall,cppcheck,cpack_suffix:
160+
longopts=${longopts},no-aio-intrinsics
155161

156162
ARGS=$(${getopt_prog} --longoptions $longopts --options xhf:c:pg -- "$@")
157163
if [ $? -ne 0 ] ; then
@@ -219,6 +225,10 @@ while true; do
219225
generateArgs="${generateArgs} -DWITH_VIDEO:Bool=Off"
220226
shift
221227
;;
228+
--no-aio-intrinsics)
229+
generateArgs="${generateArgs} -DDONT_USE_AIO_INTRINSICS:Bool=On"
230+
shift
231+
;;
222232
--notest)
223233
notest=yes
224234
shift

cmake/pthreads-dep.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ if (WITH_ASYNC)
6969
else ()
7070
target_compile_definitions(thread_lib INTERFACE DONT_USE_READER_THREAD)
7171
endif ()
72+
73+
if (DONT_USE_AIO_INTRINSICS)
74+
target_compile_definitions(thread_lib INTERFACE DONT_USE_AIO_INTRINSICS)
75+
endif ()
7276
else (WITH_ASYNC)
7377
target_compile_definitions(thread_lib INTERFACE DONT_USE_READER_THREAD)
7478
set(THREADING_PKG_STATUS "asynchronous I/O disabled.")

scp.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -387,12 +387,12 @@ int sim_aio_update_queue (void)
387387
int migrated = 0;
388388

389389
AIO_ILOCK;
390-
if (AIO_QUEUE_VAL != QUEUE_LIST_END) { /* List !Empty */
390+
if (AIO_QUEUE_VAL() != QUEUE_LIST_END) { /* List !Empty */
391391
UNIT *q, *uptr;
392392
int32 a_event_time;
393393
do { /* Grab current queue */
394-
q = AIO_QUEUE_VAL;
395-
} while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q));
394+
q = AIO_QUEUE_VAL();
395+
} while (!AIO_QUEUE_SET(QUEUE_LIST_END, q));
396396
while (q != QUEUE_LIST_END) { /* List !Empty */
397397
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);
398398
++migrated;
@@ -406,13 +406,16 @@ if (AIO_QUEUE_VAL != QUEUE_LIST_END) { /* List !Empty */
406406
}
407407
else
408408
a_event_time = uptr->a_event_time;
409-
AIO_IUNLOCK;
409+
/* Note: Commented out and not deleted. So far, SIMH doesn't appear to
410+
* attempt to reacquire the simh_asynch_lock mutex across threads.
411+
* Reacquiring the mutex would potentially cause a deadlock across threads. */
412+
/*AIO_IUNLOCK;*/
410413
uptr->a_activate_call (uptr, a_event_time);
411414
if (uptr->a_check_completion) {
412415
sim_debug (SIM_DBG_AIO_QUEUE, &sim_scp_dev, "Calling Completion Check for asynch event on %s\n", sim_uname(uptr));
413416
uptr->a_check_completion (uptr);
414417
}
415-
AIO_ILOCK;
418+
/*AIO_ILOCK;*/
416419
}
417420
}
418421
AIO_IUNLOCK;
@@ -431,12 +434,11 @@ else {
431434
uptr->a_event_time = event_time;
432435
uptr->a_activate_call = caller;
433436
do {
434-
q = AIO_QUEUE_VAL;
437+
q = AIO_QUEUE_VAL();
435438
uptr->a_next = q; /* Mark as on list */
436-
} while (q != AIO_QUEUE_SET(uptr, q));
439+
} while (!AIO_QUEUE_SET(uptr, q));
437440
}
438441
AIO_IUNLOCK;
439-
sim_asynch_check = 0; /* try to force check */
440442
if (sim_idle_wait) {
441443
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);
442444
pthread_cond_signal (&sim_asynch_wake);

0 commit comments

Comments
 (0)