Merge pull request #3266 from juj/pthreads

Pthreads
This commit is contained in:
juj 2015-06-03 03:09:26 +03:00
Родитель f57b587eec 8c6d6f53fe
Коммит 6dc579b94d
142 изменённых файлов: 9408 добавлений и 263 удалений

27
emcc
Просмотреть файл

@ -1000,6 +1000,28 @@ try:
if js_opts:
shared.Settings.RUNNING_JS_OPTS = 1
if shared.Settings.USE_PTHREADS:
if not any(s.startswith('PTHREAD_POOL_SIZE=') for s in settings_changes):
settings_changes.append('PTHREAD_POOL_SIZE=0')
js_libraries.append(shared.path_from_root('src', 'library_pthread.js'))
newargs.append('-D__EMSCRIPTEN_PTHREADS__=1')
else:
js_libraries.append(shared.path_from_root('src', 'library_pthread_stub.js'))
if shared.Settings.USE_PTHREADS:
if shared.Settings.PROXY_TO_WORKER:
logging.error('-s PROXY_TO_WORKER=1 is not yet supported with -s USE_PTHREADS=1!')
exit(1)
if shared.Settings.LINKABLE:
logging.error('-s LINKABLE=1 is not supported with -s USE_PTHREADS=1!')
exit(1)
if shared.Settings.SIDE_MODULE:
logging.error('-s SIDE_MODULE=1 is not supported with -s USE_PTHREADS=1!')
exit(1)
if shared.Settings.MAIN_MODULE:
logging.error('-s MAIN_MODULE=1 is not supported with -s USE_PTHREADS=1!')
exit(1)
shared.Settings.EMSCRIPTEN_VERSION = shared.EMSCRIPTEN_VERSION
shared.Settings.OPT_LEVEL = opt_level
shared.Settings.DEBUG_LEVEL = debug_level
@ -1351,6 +1373,9 @@ try:
final += '.mem.js'
src = None
if shared.Settings.USE_PTHREADS:
shutil.copyfile(shared.path_from_root('src', 'pthread-main.js'), os.path.join(os.path.dirname(os.path.abspath(target)), 'pthread-main.js'))
log_time('source transforms')
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
@ -1606,7 +1631,7 @@ try:
<script>
if ((',' + window.location.search.substr(1) + ',').indexOf(',noProxy,') < 0) {
console.log('running code in a web worker');
''' + open(shared.path_from_root('src', 'webGLClient.js')).read() + '\n' + open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', child_js).replace('{{{ IDBStore.js }}}', open(shared.path_from_root('src', 'IDBStore.js')).read()) + '''
''' + open(shared.path_from_root('src', 'webGLClient.js')).read() + '\n' + shared.read_and_preprocess(shared.path_from_root('src', 'proxyClient.js')).replace('{{{ filename }}}', child_js).replace('{{{ IDBStore.js }}}', open(shared.path_from_root('src', 'IDBStore.js')).read()) + '''
} else {
// note: no support for code mods (PRECISE_F32==2)
console.log('running code on the main thread');

Просмотреть файл

@ -65,6 +65,8 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
backend_args = [backend_compiler, infile, '-march=js', '-filetype=asm', '-o', temp_js]
if settings['PRECISE_F32']:
backend_args += ['-emscripten-precise-f32']
if settings['USE_PTHREADS']:
backend_args += ['-emscripten-enable-pthreads']
if settings['WARN_UNALIGNED']:
backend_args += ['-emscripten-warn-unaligned']
if settings['RESERVED_FUNCTION_POINTERS'] > 0:
@ -220,8 +222,8 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
staticbump = mem_init.count(',')+1
while staticbump % 16 != 0: staticbump += 1
pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + %d;
/* global initializers */ __ATINIT__.push(%s);
%s''' % (staticbump, global_initializers, mem_init)) # XXX wrong size calculation!
/* global initializers */ %s __ATINIT__.push(%s);
%s''' % (staticbump, 'if (!ENVIRONMENT_IS_PTHREAD)' if settings['USE_PTHREADS'] else '', global_initializers, mem_init)) # XXX wrong size calculation!
if settings['SIDE_MODULE']:
pre = pre.replace('Runtime.GLOBAL_BASE', 'gb').replace('{{{ STATIC_BUMP }}}', str(staticbump))
@ -460,7 +462,12 @@ function _emscripten_asm_const_%d(%s) {
'shiftRightArithmeticByScalar',
'shiftRightLogicalByScalar',
'shiftLeftByScalar'];
fundamentals = ['Math', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array', 'NaN', 'Infinity']
fundamentals = ['Math']
if settings['USE_PTHREADS']:
fundamentals += ['SharedInt8Array', 'SharedInt16Array', 'SharedInt32Array', 'SharedUint8Array', 'SharedUint16Array', 'SharedUint32Array', 'SharedFloat32Array', 'SharedFloat64Array', 'Atomics']
else:
fundamentals += ['Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array']
fundamentals += ['NaN', 'Infinity']
if metadata['simd']:
fundamentals += ['SIMD']
if settings['ALLOW_MEMORY_GROWTH']: fundamentals.append('byteLength')
@ -523,7 +530,7 @@ function _emscripten_asm_const_%d(%s) {
if not settings['SIDE_MODULE']:
asm_setup += 'var gb = Runtime.GLOBAL_BASE, fb = 0;\n'
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew']
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', 'setThrew']
if not settings['RELOCATABLE']:
asm_runtime_funcs += ['setTempRet0', 'getTempRet0']
else:
@ -649,6 +656,10 @@ function ftCall_%s(%s) {%s
asm_global_funcs += ''.join([' var SIMD_' + ty + '=global' + access_quote('SIMD') + access_quote(ty) + ';\n' for ty in simdtypes])
asm_global_funcs += ''.join([' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simdinttypes for g in simdintfuncs])
asm_global_funcs += ''.join([' var SIMD_' + ty + '_' + g + '=SIMD_' + ty + access_quote(g) + ';\n' for ty in simdfloattypes for g in simdfloatfuncs])
if settings['USE_PTHREADS']:
# asm_global_funcs += ''.join([' var Atomics_' + ty + '=global' + access_quote('Atomics') + access_quote(ty) + ';\n' for ty in ['load', 'store', 'exchange', 'compareExchange', 'add', 'sub', 'and', 'or', 'xor', 'fence']])
# TODO: Once bug https://bugzilla.mozilla.org/show_bug.cgi?id=1141986 is implemented, replace the following line with the above one!
asm_global_funcs += ''.join([' var Atomics_' + ty + '=global' + access_quote('Atomics') + access_quote(ty) + ';\n' for ty in ['load', 'store', 'compareExchange', 'add', 'sub', 'and', 'or', 'xor', 'fence']])
asm_global_vars = ''.join([' var ' + g + '=env' + access_quote(g) + '|0;\n' for g in basic_vars + global_vars])
# sent data
@ -724,14 +735,15 @@ var asm = (function(global, env, buffer) {
var HEAPU32 = new global%s(buffer);
var HEAPF32 = new global%s(buffer);
var HEAPF64 = new global%s(buffer);
''' % (access_quote('Int8Array'),
access_quote('Int16Array'),
access_quote('Int32Array'),
access_quote('Uint8Array'),
access_quote('Uint16Array'),
access_quote('Uint32Array'),
access_quote('Float32Array'),
access_quote('Float64Array')) if not settings['ALLOW_MEMORY_GROWTH'] else '''
''' % (access_quote('SharedInt8Array' if settings['USE_PTHREADS'] else 'Int8Array'),
access_quote('SharedInt16Array' if settings['USE_PTHREADS'] else 'Int16Array'),
access_quote('SharedInt32Array' if settings['USE_PTHREADS'] else 'Int32Array'),
access_quote('SharedUint8Array' if settings['USE_PTHREADS'] else 'Uint8Array'),
access_quote('SharedUint16Array' if settings['USE_PTHREADS'] else 'Uint16Array'),
access_quote('SharedUint32Array' if settings['USE_PTHREADS'] else 'Uint32Array'),
access_quote('SharedFloat32Array' if settings['USE_PTHREADS'] else 'Float32Array'),
access_quote('SharedFloat64Array' if settings['USE_PTHREADS'] else 'Float64Array'))
if not settings['ALLOW_MEMORY_GROWTH'] else '''
var Int8View = global%s;
var Int16View = global%s;
var Int32View = global%s;
@ -800,6 +812,12 @@ function stackRestore(top) {
top = top|0;
STACKTOP = top;
}
function establishStackSpace(stackBase, stackMax) {
stackBase = stackBase|0;
stackMax = stackMax|0;
STACKTOP = stackBase;
STACK_MAX = stackMax;
}
''' + ('''
function setAsync() {
___async = 1;
@ -869,6 +887,7 @@ function getTempRet0() {
Runtime.stackAlloc = asm['stackAlloc'];
Runtime.stackSave = asm['stackSave'];
Runtime.stackRestore = asm['stackRestore'];
Runtime.establishStackSpace = asm['establishStackSpace'];
''')
if not settings['RELOCATABLE']:
funcs_js.append('''

Просмотреть файл

@ -0,0 +1,52 @@
.. Pthreads support:
==============================
Pthreads support
==============================
Low level multithreading is currently supported in Firefox Nightly release channel via an experimental extension to JavaScript Web Workers. This is a work-in-progress research project, and the prototype specification can be located `here <https://docs.google.com/document/d/1NDGA_gZJ7M7w1Bh8S0AoDyEqwDdRh4uSoTPSNn77PFk/edit?usp=sharing>`.
The proposed specification allows Emscripten applications to share the main memory heap between web workers. This along with primitives for low level atomics and futex support enables Emscripten to implement support for the Pthreads (POSIX threads) API.
Compiling with pthreads enabled
===============================
By default, support for pthreads is not enabled, since the specification is still in a prototyping stage. To enable code generation for pthreads, the following command line flags exist:
- Pass the compiler flag -s USE_PTHREADS=1 when compiling any .c/.cpp files, AND when linking to generate the final output .js file.
- Optionally, pass the linker flag -s PTHREAD_POOL_SIZE=<integer> to specify a predefined pool of web workers to populate at page preRun time before application main() is called. If -1 is passed to both PTHREAD_POOL_SIZE and PTHREAD_HINT_NUM_CORES, then a popup dialog will ask the user the size of the pool (useful for testing).
- Optionally, pass the linker flag -s PTHREAD_HINT_NUM_CORES=<integer> to choose what the function emscripten_num_logical_cores(); will return if navigator.hardwareConcurrency is not supported. If -1 is specified here, a popup dialog will be shown at startup to let the user specify the value that is returned here. This can be helpful in order to dynamically test how an application behaves with different values here.
There should be no other changes required. In C/C++ code, the preprocessor check #ifdef __EMSCRIPTEN_PTHREADS__ can be used to detect whether Emscripten is currently targeting pthreads.
Special considerations
======================
The Emscripten implementation for the pthreads API should follow the POSIX standard closely, but some behavioral differences do exist:
- When -s PTHREAD_POOL_SIZE=<integer> is not specified and pthread_create() is called, the new thread will not actually start to run immediately, but the main JS thread must yield execution back to browser first. This behavior is a result of `#1049079 <https://bugzilla.mozilla.org/show_bug.cgi?id=1049079>`.
- Currently several of the functions in the C runtime, such as filesystem functions like fopen(), fread(), printf(), fprintf() etc. are not multithreaded, but instead their execution is proxied over to the main application thread. Memory allocation via malloc() and free() is fully multithreaded though.
- The Emscripten implementation does not support `POSIX signals <http://man7.org/linux/man-pages/man7/signal.7.html>`, which are sometimes used in conjunction with pthreads. This is because it is not possible to send signals to web workers and pre-empt their execution. The only exception to this is pthread_kill() which can be used as normal to forcibly terminate a running thread.
- The Emscripten implementation does also not support multiprocessing via fork() and join().
- For web security purposes, there exists a fixed limit (by default 20) of threads that can be spawned when running in Firefox Nightly. `#1052398 <https://bugzilla.mozilla.org/show_bug.cgi?id=1052398>`. To adjust the limit, navigate to about:config and change the value of the pref "dom.workers.maxPerDomain".
- Some of the features in the pthreads specification are unsupported since the upstream musl library that Emscripten utilizes does not support them, or they are marked optional and a conformant implementation need not support them. Such unsupported features in Emscripten include prioritization of threads, and pthread_rwlock_unlock() is not performed in thread priority order. The functions pthread_mutexattr_set/getprotocol(), pthread_mutexattr_set/getprioceiling() and pthread_attr_set/getscope() are no-ops.
- One particular note to pay attention to when porting is that sometimes in existing codebases the callback function pointers to pthread_create() and pthread_cleanup_push() omit the void* argument, which strictly speaking is undefined behavior in C/C++, but works in several x86 calling conventions. Doing this in Emscripten will issue a compiler warning, and can abort at runtime when attempting to call a function pointer with incorrect signature, so in the presence of such errors, it is good to check the signatures of the thread callback functions.
Also note that when compiling code that uses pthreads, an additional JavaScript file `pthread-main.js` is generated alongside the output .js file. That file must be deployed with the rest of the generated code files.
Running code and tests
======================
Any code that is compiled with pthreads support enabled will currently only work in the Firefox Nightly channel, since the SharedArrayBuffer specification is still in an experimental research stage before standardization. There exists two test suites that can be used to verify the behavior of the pthreads API implementation in Emscripten:
- The Emscripten unit test suite contains several pthreads-specific tests in the "browser." suite. Run any of the tests named browser.test_pthread_*.
- An Emscripten-specialized version of the `Open POSIX Test Suite <http://posixtest.sourceforge.net/>` is available at `juj/posixtestsuite <https://github.com/juj/posixtestsuite>` GitHub repository. This suite contains about 300 tests for pthreads conformance. To run this suite, the pref dom.workers.maxPerDomain should first be increased to at least 50.
Please check these first in case of any issues. Bugs can be reported to the Emscripten bug tracker as usual.

Просмотреть файл

@ -194,7 +194,7 @@ if (!BOOTSTRAPPING_STRUCT_INFO) {
load('modules.js');
load('parseTools.js');
load('jsifier.js');
globalEval(processMacros(preprocess(read('runtime.js'))));
globalEval(processMacros(preprocess(read('runtime.js'), 'runtime.js')));
Runtime.QUANTUM_SIZE = QUANTUM_SIZE;
//===============================

Просмотреть файл

@ -32,6 +32,7 @@
"eglGetProcAddress": ["emscripten_GetProcAddress"],
"glfwGetProcAddress": ["emscripten_GetProcAddress"],
"emscripten_GetProcAddress": ["strstr"],
"__cxa_begin_catch": ["__cxa_can_catch", "__cxa_is_pointer_type"]
"__cxa_begin_catch": ["__cxa_can_catch", "__cxa_is_pointer_type"],
"sleep": ["usleep"]
}

Просмотреть файл

@ -1,23 +1,25 @@
function emrun_register_handlers() {
function post(msg) {
var http = new XMLHttpRequest();
http.open("POST", "stdio.html", true);
http.send(msg);
if (typeof window === "object" && !ENVIRONMENT_IS_PTHREAD) {
function emrun_register_handlers() {
function post(msg) {
var http = new XMLHttpRequest();
http.open("POST", "stdio.html", true);
http.send(msg);
}
// If the address contains localhost, or we are running the page from port 6931, we can assume we're running the test runner and should post stdout logs.
if (document.URL.search("localhost") != -1 || document.URL.search(":6931/") != -1) {
var emrun_http_sequence_number = 1;
var prevPrint = Module['print'];
var prevErr = Module['printErr'];
function emrun_exit() { post('^exit^'+EXITSTATUS); };
Module['addOnExit'](emrun_exit);
Module['print'] = function emrun_print(text) { post('^out^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevPrint(text); }
Module['printErr'] = function emrun_printErr(text) { post('^err^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevErr(text); }
// Notify emrun web server that this browser has successfully launched the page.
post('^pageload^');
}
}
// If the address contains localhost, or we are running the page from port 6931, we can assume we're running the test runner and should post stdout logs.
if (document.URL.search("localhost") != -1 || document.URL.search(":6931/") != -1) {
var emrun_http_sequence_number = 1;
var prevPrint = Module['print'];
var prevErr = Module['printErr'];
function emrun_exit() { post('^exit^'+EXITSTATUS); };
Module['addOnExit'](emrun_exit);
Module['print'] = function emrun_print(text) { post('^out^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevPrint(text); }
Module['printErr'] = function emrun_printErr(text) { post('^err^'+(emrun_http_sequence_number++)+'^'+encodeURIComponent(text)); prevErr(text); }
}
// Notify emrun web server that this browser has successfully launched the page.
post('^pageload^');
}
window.addEventListener('load', emrun_register_handlers);
// POSTs the given binary data represented as a (typed) array data back to the emrun-based web server.
// To use from C code, call e.g. EM_ASM_({emrun_file_dump("file.dat", HEAPU8.subarray($0, $0 + $1));}, my_data_pointer, my_data_pointer_byte_length);

Просмотреть файл

@ -46,9 +46,9 @@ function JSify(data, functionsOnly) {
// things out as they are ready.
var shellParts = read(shellFile).split('{{BODY}}');
print(processMacros(preprocess(shellParts[0])));
print(processMacros(preprocess(shellParts[0], shellFile)));
var preFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'preamble_sharedlib.js' : 'preamble.js';
var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime())));
var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()), preFile));
print(pre);
}
@ -204,7 +204,15 @@ function JSify(data, functionsOnly) {
});
if (VERBOSE) printErr('adding ' + finalName + ' and deps ' + deps + ' : ' + (snippet + '').substr(0, 40));
var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
var contentText = isFunction ? snippet : ('var ' + finalName + '=' + snippet + ';');
var contentText;
if (isFunction) {
contentText = snippet;
} else if (typeof snippet === 'string' && snippet.indexOf(';') == 0) {
contentText = 'var ' + finalName + snippet;
if (snippet[snippet.length-1] != ';' && snippet[snippet.length-1] != '}') contentText += ';';
} else {
contentText = 'var ' + finalName + '=' + snippet + ';';
}
var sig = LibraryManager.library[ident + '__sig'];
if (isFunction && sig && LibraryManager.library[ident + '__asm']) {
// asm library function, add it as generated code alongside the generated code
@ -302,13 +310,24 @@ function JSify(data, functionsOnly) {
return true;
});
// write out the singleton big memory initialization value
if (USE_PTHREADS) {
print('if (!ENVIRONMENT_IS_PTHREAD) {') // Pthreads should not initialize memory again, since it's shared with the main thread.
}
print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE' + (SIDE_MODULE ? '+H_BASE' : ''), true));
if (USE_PTHREADS) {
print('}')
}
} else {
print('/* no memory initializer */'); // test purposes
}
if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n');
if (USE_PTHREADS) {
print('var tempDoublePtr;\n');
print('if (!ENVIRONMENT_IS_PTHREAD) tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n');
} else {
print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n');
}
print('assert(tempDoublePtr % 8 == 0);\n');
print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n');
print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n');
@ -422,13 +441,13 @@ function JSify(data, functionsOnly) {
print(read('deterministic.js'));
}
var postFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'postamble_sharedlib.js' : 'postamble.js';
var postParts = processMacros(preprocess(read(postFile))).split('{{GLOBAL_VARS}}');
var postParts = processMacros(preprocess(read(postFile), postFile)).split('{{GLOBAL_VARS}}');
print(postParts[0]);
print(postParts[1]);
var shellParts = read(shellFile).split('{{BODY}}');
print(processMacros(preprocess(shellParts[1])));
print(processMacros(preprocess(shellParts[1], shellFile)));
// Print out some useful metadata
if (RUNNING_JS_OPTS || PGO) {
var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions));

Разница между файлами не показана из-за своего большого размера Загрузить разницу

683
src/library_pthread.js Normal file
Просмотреть файл

@ -0,0 +1,683 @@
var LibraryPThread = {
$PThread__postset: 'if (!ENVIRONMENT_IS_PTHREAD) PThread.initMainThreadBlock();',
$PThread__deps: ['$PROCINFO'],
$PThread: {
MAIN_THREAD_ID: 1, // A special constant that identifies the main JS thread ID.
mainThreadInfo: {
schedPolicy: 0/*SCHED_OTHER*/,
schedPrio: 0
},
// Since creating a new Web Worker is so heavy (it must reload the whole compiled script page!), maintain a pool of such
// workers that have already parsed and loaded the scripts.
unusedWorkerPool: [],
// The currently executing pthreads.
runningWorkers: [],
// Points to a pthread_t structure in the Emscripten main heap, allocated on demand if/when first needed.
// mainThreadBlock: undefined,
initMainThreadBlock: function() {
if (ENVIRONMENT_IS_PTHREAD) return undefined;
PThread.mainThreadBlock = allocate({{{ C_STRUCTS.pthread.__size__ }}}, "i32*", ALLOC_STATIC);
for (var i = 0; i < {{{ C_STRUCTS.pthread.__size__ }}}/4; ++i) HEAPU32[PThread.mainThreadBlock/4+i] = 0;
// The pthread struct has a field that points to itself - this is used as a magic ID to detect whether the pthread_t
// structure is 'alive'.
{{{ makeSetValue('PThread.mainThreadBlock', C_STRUCTS.pthread.self, 'PThread.mainThreadBlock', 'i32') }}};
// Allocate memory for thread-local storage.
var tlsMemory = allocate({{{ cDefine('PTHREAD_KEYS_MAX') }}} * 4, "i32*", ALLOC_STATIC);
for (var i = 0; i < {{{ cDefine('PTHREAD_KEYS_MAX') }}}; ++i) HEAPU32[tlsMemory/4+i] = 0;
Atomics.store(HEAPU32, (PThread.mainThreadBlock + {{{ C_STRUCTS.pthread.tsd }}} ) >> 2, tlsMemory); // Init thread-local-storage memory array.
Atomics.store(HEAPU32, (PThread.mainThreadBlock + {{{ C_STRUCTS.pthread.tid }}} ) >> 2, PThread.mainThreadBlock); // Main thread ID.
Atomics.store(HEAPU32, (PThread.mainThreadBlock + {{{ C_STRUCTS.pthread.pid }}} ) >> 2, PROCINFO.pid); // Process ID.
},
// Maps pthread_t to pthread info objects
pthreads: {},
pthreadIdCounter: 2, // 0: invalid thread, 1: main JS UI thread, 2+: IDs for pthreads
exitHandlers: null, // An array of C functions to run when this thread exits.
runExitHandlers: function() {
if (PThread.exitHandlers !== null) {
while (PThread.exitHandlers.length > 0) {
PThread.exitHandlers.pop()();
}
PThread.exitHandlers = null;
}
// Call into the musl function that runs destructors of all thread-specific data.
if (ENVIRONMENT_IS_PTHREAD && threadInfoStruct) ___pthread_tsd_run_dtors();
},
// Called when we are performing a pthread_exit(), either explicitly called by programmer,
// or implicitly when leaving the thread main function.
threadExit: function(exitCode) {
var tb = _pthread_self();
if (tb) { // If we haven't yet exited?
Atomics.store(HEAPU32, (tb + {{{ C_STRUCTS.pthread.threadExitCode }}} ) >> 2, exitCode);
// When we publish this, the main thread is free to deallocate the thread object and we are done.
// Therefore set threadInfoStruct = 0; above to 'release' the object in this worker thread.
Atomics.store(HEAPU32, (tb + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2, 1);
// Disable all cancellation so that executing the cleanup handlers won't trigger another JS
// canceled exception to be thrown.
Atomics.store(HEAPU32, (tb + {{{ C_STRUCTS.pthread.canceldisable }}} ) >> 2, 1/*PTHREAD_CANCEL_DISABLE*/);
Atomics.store(HEAPU32, (tb + {{{ C_STRUCTS.pthread.cancelasync }}} ) >> 2, 0/*PTHREAD_CANCEL_DEFERRED*/);
PThread.runExitHandlers();
_emscripten_futex_wake(tb + {{{ C_STRUCTS.pthread.threadStatus }}}, {{{ cDefine('INT_MAX') }}});
threadInfoStruct = 0;
if (ENVIRONMENT_IS_PTHREAD) {
postMessage({ cmd: 'exit' });
}
}
},
threadCancel: function() {
PThread.runExitHandlers();
Atomics.store(HEAPU32, (threadInfoStruct + {{{ C_STRUCTS.pthread.threadExitCode }}} ) >> 2, -1/*PTHREAD_CANCELED*/);
Atomics.store(HEAPU32, (threadInfoStruct + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2, 1); // Mark the thread as no longer running.
_emscripten_futex_wake(threadInfoStruct + {{{ C_STRUCTS.pthread.threadStatus }}}, {{{ cDefine('INT_MAX') }}}); // wake all threads
threadInfoStruct = selfThreadId = 0; // Not hosting a pthread anymore in this worker, reset the info structures to null.
postMessage({ cmd: 'cancelDone' });
},
terminateAllThreads: function() {
for (var t in PThread.pthreads) {
var pthread = PThread.pthreads[t];
if (pthread) {
PThread.freeThreadData(pthread);
if (pthread.worker) pthread.worker.terminate();
}
}
PThread.pthreads = {};
for (var t in PThread.unusedWorkerPool) {
var pthread = PThread.unusedWorkerPool[t];
if (pthread) {
PThread.freeThreadData(pthread);
if (pthread.worker) pthread.worker.terminate();
}
}
PThread.unusedWorkerPool = [];
for (var t in PThread.runningWorkers) {
var pthread = PThread.runningWorkers[t];
if (pthread) {
PThread.freeThreadData(pthread);
if (pthread.worker) pthread.worker.terminate();
}
}
PThread.runningWorkers = [];
},
freeThreadData: function(pthread) {
if (!pthread) return;
if (pthread.threadInfoStruct) {
var tlsMemory = {{{ makeGetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.tsd, 'i32') }}};
{{{ makeSetValue('pthread.threadInfoStruct', C_STRUCTS.pthread.tsd, 0, 'i32') }}};
_free(pthread.tlsMemory);
_free(pthread.threadInfoStruct);
}
pthread.threadInfoStruct = 0;
if (pthread.allocatedOwnStack && pthread.stackBase) _free(pthread.stackBase);
pthread.stackBase = 0;
if (pthread.worker) pthread.worker.pthread = null;
},
// Allocates a the given amount of new web workers and stores them in the pool of unused workers.
// onFinishedLoading: A callback function that will be called once all of the workers have been initialized and are
// ready to host pthreads. Optional. This is used to mitigate bug https://bugzilla.mozilla.org/show_bug.cgi?id=1049079
allocateUnusedWorkers: function(numWorkers, onFinishedLoading) {
Module['print']('Preallocating ' + numWorkers + ' workers for a pthread spawn pool.');
var numWorkersLoaded = 0;
for (var i = 0; i < numWorkers; ++i) {
var worker = new Worker('pthread-main.js');
worker.onmessage = function(e) {
if (e.data.cmd === 'processQueuedMainThreadWork') {
// TODO: Must post message to main Emscripten thread in PROXY_TO_WORKER mode.
_emscripten_main_thread_process_queued_calls();
} else if (e.data.cmd === 'spawnThread') {
__spawn_thread(e.data);
} else if (e.data.cmd === 'cleanupThread') {
__cleanup_thread(e.data.thread);
} else if (e.data.cmd === 'killThread') {
__kill_thread(e.data.thread);
} else if (e.data.cmd === 'cancelThread') {
__cancel_thread(e.data.thread);
} else if (e.data.cmd === 'loaded') {
++numWorkersLoaded;
if (numWorkersLoaded === numWorkers && onFinishedLoading) {
onFinishedLoading();
}
} else if (e.data.cmd === 'print') {
Module['print']('Thread ' + e.data.threadId + ': ' + e.data.text);
} else if (e.data.cmd === 'printErr') {
Module['printErr']('Thread ' + e.data.threadId + ': ' + e.data.text);
} else if (e.data.cmd === 'exit') {
// todo
} else if (e.data.cmd === 'cancelDone') {
PThread.freeThreadData(worker.pthread);
worker.pthread = undefined; // Detach the worker from the pthread object, and return it to the worker pool as an unused worker.
PThread.unusedWorkerPool.push(worker);
// TODO: Free if detached.
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker.pthread), 1); // Not a running Worker anymore.
} else {
Module['printErr']("worker sent an unknown command " + e.data.cmd);
}
};
worker.onerror = function(e) {
Module['printErr']('pthread sent an error! ' + e.message);
};
// Allocate tempDoublePtr for the worker. This is done here on the worker's behalf, since allocate()
// is not thread-safe.
var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_NORMAL), 8); // TODO: leaks. Cleanup after worker terminates.
// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage({
cmd: 'load',
url: currentScriptUrl,
buffer: HEAPU8.buffer,
tempDoublePtr: tempDoublePtr,
PthreadWorkerInit: PthreadWorkerInit
}, [HEAPU8.buffer]);
PThread.unusedWorkerPool.push(worker);
}
},
getNewWorker: function() {
if (PThread.unusedWorkerPool.length == 0) PThread.allocateUnusedWorkers(1);
if (PThread.unusedWorkerPool.length > 0) return PThread.unusedWorkerPool.pop();
else return null;
},
busySpinWait: function(msecs) {
var t = performance.now() + msecs;
while(performance.now() < t) {
;
}
}
},
_kill_thread: function(pthread_ptr) {
if (ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! _kill_thread() can only ever be called from main JS thread!';
if (!pthread_ptr) throw 'Internal Error! Null pthread_ptr in _kill_thread!';
{{{ makeSetValue('pthread_ptr', C_STRUCTS.pthread.self, 0, 'i32') }}};
var pthread = PThread.pthreads[pthread_ptr];
pthread.worker.terminate();
PThread.freeThreadData(pthread);
// The worker was completely nuked (not just the pthread execution it was hosting), so remove it from running workers
// but don't put it back to the pool.
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(pthread.worker.pthread), 1); // Not a running Worker anymore.
pthread.worker.pthread = undefined;
},
_cleanup_thread: function(pthread_ptr) {
if (ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! _cleanup_thread() can only ever be called from main JS thread!';
if (!pthread_ptr) throw 'Internal Error! Null pthread_ptr in _cleanup_thread!';
{{{ makeSetValue('pthread_ptr', C_STRUCTS.pthread.self, 0, 'i32') }}};
var pthread = PThread.pthreads[pthread_ptr];
var worker = pthread.worker;
PThread.freeThreadData(pthread);
worker.pthread = undefined; // Detach the worker from the pthread object, and return it to the worker pool as an unused worker.
PThread.unusedWorkerPool.push(worker);
PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker.pthread), 1); // Not a running Worker anymore.
},
_cancel_thread: function(pthread_ptr) {
if (ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! _cancel_thread() can only ever be called from main JS thread!';
if (!pthread_ptr) throw 'Internal Error! Null pthread_ptr in _cancel_thread!';
var pthread = PThread.pthreads[pthread_ptr];
pthread.worker.postMessage({ cmd: 'cancel' });
},
_spawn_thread: function(threadParams) {
if (ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_PTHREAD) throw 'Internal Error! _spawn_thread() can only ever be called from main JS thread!';
var worker = PThread.getNewWorker();
if (worker.pthread !== undefined) throw 'Internal error!';
if (!threadParams.pthread_ptr) throw 'Internal error, no pthread ptr!';
PThread.runningWorkers.push(worker);
// Allocate memory for thread-local storage and initialize it to zero.
var tlsMemory = _malloc({{{ cDefine('PTHREAD_KEYS_MAX') }}} * 4);
for (var i = 0; i < {{{ cDefine('PTHREAD_KEYS_MAX') }}}; ++i) {
{{{ makeSetValue('tlsMemory', 'i*4', 0, 'i32') }}};
}
var pthread = PThread.pthreads[threadParams.pthread_ptr] = { // Create a pthread info object to represent this thread.
worker: worker,
stackBase: threadParams.stackBase,
stackSize: threadParams.stackSize,
allocatedOwnStack: threadParams.allocatedOwnStack,
thread: threadParams.pthread_ptr,
threadInfoStruct: threadParams.pthread_ptr // Info area for this thread in Emscripten HEAP (shared)
};
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2, 0); // threadStatus <- 0, meaning not yet exited.
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.threadExitCode }}} ) >> 2, 0); // threadExitCode <- 0.
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.detached }}} ) >> 2, threadParams.detached);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.tsd }}} ) >> 2, tlsMemory); // Init thread-local-storage memory array.
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.tsd_used }}} ) >> 2, 0); // Mark initial status to unused.
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.tid }}} ) >> 2, pthread.threadInfoStruct); // Main thread ID.
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.pid }}} ) >> 2, PROCINFO.pid); // Process ID.
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}}) >> 2, threadParams.stackSize);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.stack_size }}}) >> 2, threadParams.stackSize);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.stack }}}) >> 2, threadParams.stackBase);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 8) >> 2, threadParams.stackBase);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 12) >> 2, threadParams.detached);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 20) >> 2, threadParams.schedPolicy);
Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 24) >> 2, threadParams.schedPrio);
worker.pthread = pthread;
// Ask the worker to start executing its pthread entry point function.
worker.postMessage({
cmd: 'run',
start_routine: threadParams.startRoutine,
arg: threadParams.arg,
threadInfoStruct: threadParams.pthread_ptr,
selfThreadId: threadParams.pthread_ptr, // TODO: Remove this since thread ID is now the same as the thread address.
stackBase: threadParams.stackBase,
stackSize: threadParams.stackSize
});
},
#if USE_PTHREADS
_num_logical_cores__deps: ['emscripten_force_num_logical_cores'],
_num_logical_cores: '; if (ENVIRONMENT_IS_PTHREAD) __num_logical_cores = PthreadWorkerInit.__num_logical_cores; else { PthreadWorkerInit.__num_logical_cores = __num_logical_cores = allocate(1, "i32*", ALLOC_STATIC); HEAPU32[__num_logical_cores>>2] = navigator["hardwareConcurrency"] || ' + {{{ PTHREAD_HINT_NUM_CORES }}} + '; }',
#else
_num_logical_cores: 'allocate(1, "i32*", ALLOC_STATIC)',
#endif
emscripten_num_logical_cores__deps: ['_num_logical_cores'],
emscripten_num_logical_cores: function() {
return {{{ makeGetValue('__num_logical_cores', 0, 'i32') }}};
},
emscripten_force_num_logical_cores: function(cores) {
{{{ makeSetValue('__num_logical_cores', 0, 'cores', 'i32') }}};
},
pthread_create__deps: ['_spawn_thread', 'pthread_getschedparam', 'pthread_self'],
pthread_create: function(pthread_ptr, attr, start_routine, arg) {
// When running in PROXY_TO_WORKER=1 mode, the pthread creation needs to be forwarded to the main browser thread, and not the main C runtime thread,
// so the following is valid only when in non-proxy-to-worker mode.
#if !PROXY_TO_WORKER
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_PTHREAD_CREATE') }}}, pthread_ptr, attr, start_routine, arg);
#endif
if (!HEAPU8.buffer instanceof SharedArrayBuffer) {
Module['printErr']('Current environment does not support SharedArrayBuffer, pthreads are not available!');
return 1;
}
if (!pthread_ptr) {
Module['printErr']('pthread_create called with a null thread pointer!');
return 1;
}
var stackSize = 0;
var stackBase = 0;
var detached = 0; // Default thread attr is PTHREAD_CREATE_JOINABLE, i.e. start as not detached.
var schedPolicy = 0; /*SCHED_OTHER*/
var schedPrio = 0;
if (attr) {
stackSize = {{{ makeGetValue('attr', 0, 'i32') }}};
stackBase = {{{ makeGetValue('attr', 8, 'i32') }}};
detached = {{{ makeGetValue('attr', 12/*_a_detach*/, 'i32') }}} != 0/*PTHREAD_CREATE_JOINABLE*/;
var inheritSched = {{{ makeGetValue('attr', 16/*_a_sched*/, 'i32') }}} == 0/*PTHREAD_INHERIT_SCHED*/;
if (inheritSched) {
var prevSchedPolicy = {{{ makeGetValue('attr', 20/*_a_policy*/, 'i32') }}};
var prevSchedPrio = {{{ makeGetValue('attr', 24/*_a_prio*/, 'i32') }}};
_pthread_getschedparam(_pthread_self(), attr + 20, attr + 24);
schedPolicy = {{{ makeGetValue('attr', 20/*_a_policy*/, 'i32') }}};
schedPrio = {{{ makeGetValue('attr', 24/*_a_prio*/, 'i32') }}};
{{{ makeSetValue('attr', 20/*_a_policy*/, 'prevSchedPolicy', 'i32') }}};
{{{ makeSetValue('attr', 24/*_a_prio*/, 'prevSchedPrio', 'i32') }}};
} else {
schedPolicy = {{{ makeGetValue('attr', 20/*_a_policy*/, 'i32') }}};
schedPrio = {{{ makeGetValue('attr', 24/*_a_prio*/, 'i32') }}};
}
}
stackSize += 81920 /*DEFAULT_STACK_SIZE*/;
var allocatedOwnStack = stackBase == 0; // If allocatedOwnStack == true, then the pthread impl maintains the stack allocation.
if (allocatedOwnStack) {
stackBase = _malloc(stackSize); // Allocate a stack if the user doesn't want to place the stack in a custom memory area.
} else {
// Musl stores the stack base address assuming stack grows downwards, so adjust it to Emscripten convention that the
// stack grows upwards instead.
stackBase -= stackSize;
assert(stackBase > 0);
}
// Allocate thread block (pthread_t structure).
var threadInfoStruct = _malloc({{{ C_STRUCTS.pthread.__size__ }}});
for (var i = 0; i < {{{ C_STRUCTS.pthread.__size__ }}} >> 2; ++i) HEAPU32[(threadInfoStruct>>2) + i] = 0; // zero-initialize thread structure.
{{{ makeSetValue('pthread_ptr', 0, 'threadInfoStruct', 'i32') }}};
// The pthread struct has a field that points to itself - this is used as a magic ID to detect whether the pthread_t
// structure is 'alive'.
{{{ makeSetValue('threadInfoStruct', C_STRUCTS.pthread.self, 'threadInfoStruct', 'i32') }}};
var threadParams = {
stackBase: stackBase,
stackSize: stackSize,
allocatedOwnStack: allocatedOwnStack,
schedPolicy: schedPolicy,
schedPrio: schedPrio,
detached: detached,
startRoutine: start_routine,
pthread_ptr: threadInfoStruct,
arg: arg,
};
if (ENVIRONMENT_IS_WORKER) {
// The prepopulated pool of web workers that can host pthreads is stored in the main JS thread. Therefore if a
// pthread is attempting to spawn a new thread, the thread creation must be deferred to the main JS thread.
threadParams.cmd = 'spawnThread';
postMessage(threadParams);
} else {
// We are the main thread, so we have the pthread warmup pool in this thread and can fire off JS thread creation
// directly ourselves.
__spawn_thread(threadParams);
}
return 0;
},
// TODO HACK! Remove this function, it is a JS side copy of the function pthread_testcancel() in library_pthread.c.
// Just call pthread_testcancel() everywhere.
_pthread_testcancel_js: function() {
if (!ENVIRONMENT_IS_PTHREAD) return;
if (!threadInfoStruct) return;
var cancelDisabled = Atomics.load(HEAPU32, (threadInfoStruct + {{{ C_STRUCTS.pthread.canceldisable }}} ) >> 2);
if (cancelDisabled) return;
var canceled = Atomics.load(HEAPU32, (threadInfoStruct + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2);
if (canceled == 2) throw 'Canceled!';
},
pthread_join__deps: ['_cleanup_thread', '_pthread_testcancel_js'],
pthread_join: function(thread, status) {
if (!thread) {
Module['printErr']('pthread_join attempted on a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
if (ENVIRONMENT_IS_PTHREAD && selfThreadId == thread) {
Module['printErr']('PThread ' + thread + ' is attempting to join to itself!');
return ERRNO_CODES.EDEADLK;
}
else if (!ENVIRONMENT_IS_PTHREAD && PThread.mainThreadBlock == thread) {
Module['printErr']('Main thread ' + thread + ' is attempting to join to itself!');
return ERRNO_CODES.EDEADLK;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_join attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
var detached = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.detached }}} ) >> 2);
if (detached) {
Module['printErr']('Attempted to join thread ' + thread + ', which was already detached!');
return ERRNO_CODES.EINVAL; // The thread is already detached, can no longer join it!
}
for (;;) {
var threadStatus = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2);
if (threadStatus == 1) { // Exited?
var threadExitCode = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadExitCode }}} ) >> 2);
if (status) {{{ makeSetValue('status', 0, 'threadExitCode', 'i32') }}};
Atomics.store(HEAPU32, (thread + {{{ C_STRUCTS.pthread.detached }}} ) >> 2, 1); // Mark the thread as detached.
if (!ENVIRONMENT_IS_WORKER) __cleanup_thread(thread);
else postMessage({ cmd: 'cleanupThread', thread: thread});
return 0;
}
// TODO HACK! Replace the _js variant with just _pthread_testcancel:
//_pthread_testcancel();
__pthread_testcancel_js();
// In main runtime thread (the thread that initialized the Emscripten C runtime and launched main()), assist pthreads in performing operations
// that they need to access the Emscripten main runtime for.
if (!ENVIRONMENT_IS_PTHREAD) _emscripten_main_thread_process_queued_calls();
_emscripten_futex_wait(thread + {{{ C_STRUCTS.pthread.threadStatus }}}, threadStatus, 100);
}
},
pthread_kill__deps: ['_kill_thread'],
pthread_kill: function(thread, signal) {
if (signal < 0 || signal >= 65/*_NSIG*/) return ERRNO_CODES.EINVAL;
if (thread == PThread.MAIN_THREAD_ID) {
if (signal == 0) return 0; // signal == 0 is a no-op.
Module['printErr']('Main thread (id=' + thread + ') cannot be killed with pthread_kill!');
return ERRNO_CODES.ESRCH;
}
if (!thread) {
Module['printErr']('pthread_kill attempted on a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_kill attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
if (signal != 0) {
if (!ENVIRONMENT_IS_WORKER) __kill_thread(thread);
else postMessage({ cmd: 'killThread', thread: thread});
}
return 0;
},
pthread_cancel__deps: ['_cancel_thread'],
pthread_cancel: function(thread) {
if (thread == PThread.MAIN_THREAD_ID) {
Module['printErr']('Main thread (id=' + thread + ') cannot be canceled!');
return ERRNO_CODES.ESRCH;
}
if (!thread) {
Module['printErr']('pthread_cancel attempted on a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_cancel attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
Atomics.compareExchange(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2, 0, 2); // Signal the thread that it needs to cancel itself.
if (!ENVIRONMENT_IS_WORKER) __cancel_thread(thread);
else postMessage({ cmd: 'cancelThread', thread: thread});
return 0;
},
pthread_detach: function(thread) {
if (!thread) {
Module['printErr']('pthread_detach attempted on a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_detach attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
var threadStatus = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.threadStatus }}} ) >> 2);
// Follow musl convention: detached:0 means not detached, 1 means the thread was created as detached, and 2 means that the thread was detached via pthread_detach.
var wasDetached = Atomics.compareExchange(HEAPU32, (thread + {{{ C_STRUCTS.pthread.detached }}} ) >> 2, 0, 2);
return wasDetached ? (threadStatus == 0/*running*/ ? ERRNO_CODES.EINVAL : ERRNO_CODES.ESRCH) : 0;
},
pthread_exit__deps: ['exit'],
pthread_exit: function(status) {
if (!ENVIRONMENT_IS_PTHREAD) _exit(status);
else PThread.threadExit(status);
},
// Public pthread_self() function which returns a unique ID for the thread.
pthread_self: function() {
if (ENVIRONMENT_IS_PTHREAD) return threadInfoStruct;
return PThread.mainThreadBlock; // Main JS thread.
},
emscripten_is_main_runtime_thread: function() {
return !ENVIRONMENT_IS_PTHREAD;
},
emscripten_is_main_browser_thread: function() {
return !ENVIRONMENT_IS_WORKER;
},
pthread_getschedparam: function(thread, policy, schedparam) {
if (!policy && !schedparam) return ERRNO_CODES.EINVAL;
if (!thread) {
Module['printErr']('pthread_getschedparam called with a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_getschedparam attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
var schedPolicy = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.attr }}} + 20 ) >> 2);
var schedPrio = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.attr }}} + 24 ) >> 2);
if (policy) {{{ makeSetValue('policy', 0, 'schedPolicy', 'i32') }}};
if (schedparam) {{{ makeSetValue('schedparam', 0, 'schedPrio', 'i32') }}};
return 0;
},
pthread_setschedparam: function(thread, policy, schedparam) {
if (!thread) {
Module['printErr']('pthread_setschedparam called with a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_setschedparam attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
if (!schedparam) return ERRNO_CODES.EINVAL;
var newSchedPrio = {{{ makeGetValue('schedparam', 0, 'i32') }}};
if (newSchedPrio < 0) return ERRNO_CODES.EINVAL;
if (policy == 1/*SCHED_FIFO*/ || policy == 2/*SCHED_RR*/) {
if (newSchedPrio > 99) return ERRNO_CODES.EINVAL;
} else {
if (newSchedPrio > 1) return ERRNO_CODES.EINVAL;
}
Atomics.store(HEAPU32, (thread + {{{ C_STRUCTS.pthread.attr }}} + 20) >> 2, policy);
Atomics.store(HEAPU32, (thread + {{{ C_STRUCTS.pthread.attr }}} + 24) >> 2, newSchedPrio);
return 0;
},
// Marked as obsolescent in pthreads specification: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getconcurrency.html
pthread_getconcurrency: function() {
return 0;
},
// Marked as obsolescent in pthreads specification.
pthread_setconcurrency: function(new_level) {
// no-op
return 0;
},
pthread_mutexattr_getprioceiling: function(attr, prioceiling) {
// Not supported either in Emscripten or musl, return a faked value.
if (prioceiling) {{{ makeSetValue('prioceiling', 0, 99, 'i32') }}};
return 0;
},
pthread_mutexattr_setprioceiling: function(attr, prioceiling) {
// Not supported either in Emscripten or musl, return an error.
return ERRNO_CODES.EPERM;
},
pthread_getcpuclockid: function(thread, clock_id) {
return ERRNO_CODES.ENOENT; // pthread API recommends returning this error when "Per-thread CPU time clocks are not supported by the system."
},
pthread_setschedprio: function(thread, prio) {
if (!thread) {
Module['printErr']('pthread_setschedprio called with a null thread pointer!');
return ERRNO_CODES.ESRCH;
}
var self = {{{ makeGetValue('thread', C_STRUCTS.pthread.self, 'i32') }}};
if (self != thread) {
Module['printErr']('pthread_setschedprio attempted on thread ' + thread + ', which does not point to a valid thread, or does not exist anymore!');
return ERRNO_CODES.ESRCH;
}
if (prio < 0) return ERRNO_CODES.EINVAL;
var schedPolicy = Atomics.load(HEAPU32, (thread + {{{ C_STRUCTS.pthread.attr }}} + 20 ) >> 2);
if (schedPolicy == 1/*SCHED_FIFO*/ || schedPolicy == 2/*SCHED_RR*/) {
if (prio > 99) return ERRNO_CODES.EINVAL;
} else {
if (prio > 1) return ERRNO_CODES.EINVAL;
}
Atomics.store(HEAPU32, (thread + {{{ C_STRUCTS.pthread.attr }}} + 24) >> 2, prio);
return 0;
},
pthread_cleanup_push: function(routine, arg) {
if (PThread.exitHandlers === null) {
PThread.exitHandlers = [];
if (!ENVIRONMENT_IS_PTHREAD) {
__ATEXIT__.push(function() { PThread.runExitHandlers(); });
}
}
PThread.exitHandlers.push(function() { Runtime.dynCall('vi', routine, [arg]) });
},
pthread_cleanup_pop: function(execute) {
var routine = PThread.exitHandlers.pop();
if (execute) routine();
},
// pthread_sigmask - examine and change mask of blocked signals
pthread_sigmask: function(how, set, oldset) {
Module['printErr']('pthread_sigmask() is not supported: this is a no-op.');
return 0;
},
pthread_atfork: function(prepare, parent, child) {
Module['printErr']('fork() is not supported: pthread_atfork is a no-op.');
return 0;
},
// Returns 0 on success, or one of the values -ETIMEDOUT, -EWOULDBLOCK or -EINVAL on error.
emscripten_futex_wait: function(addr, val, timeout) {
if (addr <= 0 || addr > HEAP8.length || addr&3 != 0) return -{{{ cDefine('EINVAL') }}};
// dump('futex_wait addr:' + addr + ' by thread: ' + _pthread_self() + (ENVIRONMENT_IS_PTHREAD?'(pthread)':'') + '\n');
var ret = Atomics.futexWait(HEAP32, addr >> 2, val, timeout);
// dump('futex_wait done by thread: ' + _pthread_self() + (ENVIRONMENT_IS_PTHREAD?'(pthread)':'') + '\n');
if (ret == Atomics.TIMEDOUT) return -{{{ cDefine('ETIMEDOUT') }}};
if (ret == Atomics.NOTEQUAL) return -{{{ cDefine('EWOULDBLOCK') }}};
if (ret == 0) return 0;
throw 'Atomics.futexWait returned an unexpected value ' + ret;
},
// Returns the number of threads (>= 0) woken up, or the value -EINVAL on error.
// Pass count == INT_MAX to wake up all threads.
emscripten_futex_wake: function(addr, count) {
if (addr <= 0 || addr > HEAP8.length || addr&3 != 0 || count < 0) return -{{{ cDefine('EINVAL') }}};
// dump('futex_wake addr:' + addr + ' by thread: ' + _pthread_self() + (ENVIRONMENT_IS_PTHREAD?'(pthread)':'') + '\n');
var ret = Atomics.futexWake(HEAP32, addr >> 2, count);
if (ret >= 0) return ret;
throw 'Atomics.futexWake returned an unexpected value ' + ret;
},
// Returns the number of threads (>= 0) woken up, or one of the values -EINVAL or -EAGAIN on error.
emscripten_futex_wake_or_requeue: function(addr, count, cmpValue, addr2) {
if (addr <= 0 || addr2 <= 0 || addr >= HEAP8.length || addr2 >= HEAP8.length || count < 0
|| addr&3 != 0 || addr2&3 != 0) {
return -{{{ cDefine('EINVAL') }}};
}
var ret = Atomics.futexWakeOrRequeue(HEAP32, addr >> 2, count, cmpValue, addr >> 2);
if (ret == Atomics.NOTEQUAL) return -{{{ cDefine('EAGAIN') }}};
if (ret >= 0) return ret;
throw 'Atomics.futexWakeOrRequeue returned an unexpected value ' + ret;
},
};
autoAddDeps(LibraryPThread, '$PThread');
mergeInto(LibraryManager.library, LibraryPThread);

145
src/library_pthread_stub.js Normal file
Просмотреть файл

@ -0,0 +1,145 @@
var LibraryPThreadStub = {
// ===================================================================================
// Stub implementation for pthread.h when not compiling with pthreads support enabled.
// ===================================================================================
pthread_mutex_init: function() {},
pthread_mutex_destroy: function() {},
pthread_mutexattr_init: function() {},
pthread_mutexattr_settype: function() {},
pthread_mutexattr_destroy: function() {},
pthread_mutex_lock: function() {},
pthread_mutex_unlock: function() {},
pthread_mutex_trylock: function() {
return 0;
},
pthread_mutexattr_setpshared: function(attr, pshared) {
// XXX implement if/when getpshared is required
return 0;
},
pthread_cond_init: function() {},
pthread_cond_destroy: function() {},
pthread_cond_broadcast: function() {
return 0;
},
pthread_cond_wait: function() {
return 0;
},
pthread_cond_timedwait: function() {
return 0;
},
pthread_self: function() {
//FIXME: assumes only a single thread
return 0;
},
pthread_attr_init: function(attr) {
/* int pthread_attr_init(pthread_attr_t *attr); */
//FIXME: should allocate a pthread_attr_t
return 0;
},
pthread_getattr_np: function(thread, attr) {
/* int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr); */
//FIXME: should fill in attributes of the given thread in pthread_attr_t
return 0;
},
pthread_attr_destroy: function(attr) {
/* int pthread_attr_destroy(pthread_attr_t *attr); */
//FIXME: should destroy the pthread_attr_t struct
return 0;
},
pthread_attr_getstack: function(attr, stackaddr, stacksize) {
/* int pthread_attr_getstack(const pthread_attr_t *restrict attr,
void **restrict stackaddr, size_t *restrict stacksize); */
/*FIXME: assumes that there is only one thread, and that attr is the
current thread*/
{{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}};
{{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}};
return 0;
},
pthread_once: function(ptr, func) {
if (!_pthread_once.seen) _pthread_once.seen = {};
if (ptr in _pthread_once.seen) return;
Runtime.dynCall('v', func);
_pthread_once.seen[ptr] = 1;
},
$PTHREAD_SPECIFIC: {},
$PTHREAD_SPECIFIC_NEXT_KEY: 1,
pthread_key_create__deps: ['$PTHREAD_SPECIFIC', '$PTHREAD_SPECIFIC_NEXT_KEY', '$ERRNO_CODES'],
pthread_key_create: function(key, destructor) {
if (key == 0) {
return ERRNO_CODES.EINVAL;
}
{{{ makeSetValue('key', '0', 'PTHREAD_SPECIFIC_NEXT_KEY', 'i32*') }}};
// values start at 0
PTHREAD_SPECIFIC[PTHREAD_SPECIFIC_NEXT_KEY] = 0;
PTHREAD_SPECIFIC_NEXT_KEY++;
return 0;
},
pthread_getspecific__deps: ['$PTHREAD_SPECIFIC'],
pthread_getspecific: function(key) {
return PTHREAD_SPECIFIC[key] || 0;
},
pthread_setspecific__deps: ['$PTHREAD_SPECIFIC', '$ERRNO_CODES'],
pthread_setspecific: function(key, value) {
if (!(key in PTHREAD_SPECIFIC)) {
return ERRNO_CODES.EINVAL;
}
PTHREAD_SPECIFIC[key] = value;
return 0;
},
pthread_key_delete__deps: ['$PTHREAD_SPECIFIC', '$ERRNO_CODES'],
pthread_key_delete: function(key) {
if (key in PTHREAD_SPECIFIC) {
delete PTHREAD_SPECIFIC[key];
return 0;
}
return ERRNO_CODES.EINVAL;
},
pthread_cleanup_push: function(routine, arg) {
__ATEXIT__.push(function() { Runtime.dynCall('vi', routine, [arg]) })
_pthread_cleanup_push.level = __ATEXIT__.length;
},
pthread_cleanup_pop: function() {
assert(_pthread_cleanup_push.level == __ATEXIT__.length, 'cannot pop if something else added meanwhile!');
__ATEXIT__.pop();
_pthread_cleanup_push.level = __ATEXIT__.length;
},
pthread_rwlock_init: function() {
return 0; // XXX
},
pthread_cond_signal: function() {},
pthread_equal: function() {},
pthread_join: function() {},
pthread_detach: function() {},
// When pthreads is not enabled, we can't use the Atomics futex api to do proper sleeps, so simulate a busy spin wait loop instead.
usleep: function(useconds) {
// int usleep(useconds_t useconds);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html
// We're single-threaded, so use a busy loop. Super-ugly.
var msec = useconds / 1000;
if ((ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && self['performance'] && self['performance']['now']) {
var start = self['performance']['now']();
while (self['performance']['now']() - start < msec) {
// Do nothing.
}
} else {
var start = Date.now();
while (Date.now() - start < msec) {
// Do nothing.
}
}
return 0;
}
};
mergeInto(LibraryManager.library, LibraryPThreadStub);

Просмотреть файл

@ -1,7 +1,14 @@
// 'use strict'
var funs = {
_sigalrm_handler: 0,
signal__deps: ['_sigalrm_handler'],
signal: function(sig, func) {
Module.printErr('Calling stub instead of signal()');
if (sig == 14 /*SIGALRM*/) {
__sigalrm_handler = func;
} else {
Module.printErr('Calling stub instead of signal()');
}
return 0;
},
sigemptyset: function(set) {
@ -71,12 +78,12 @@ var funs = {
return -1;
},
// http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html
alarm__deps: ['_sigalrm_handler'],
alarm: function(seconds) {
// unsigned alarm(unsigned seconds);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html
// We don't support signals, and there's no way to indicate failure, so just
// fail silently.
throw 'alarm() is not implemented yet';
setTimeout(function() {
if (__sigalrm_handler) Runtime.dynCall('vi', __sigalrm_handler, [0]);
}, seconds*1000);
},
ualarm: function() {
throw 'ualarm() is not implemented yet';
@ -96,6 +103,10 @@ var funs = {
Module.printErr('Calling stub instead of pause()');
___setErrNo(ERRNO_CODES.EINTR);
return -1;
},
sigpending: function(set) {
{{{ makeSetValue('set', 0, 0, 'i32') }}};
return 0;
}
//signalfd
//ppoll
@ -106,7 +117,6 @@ var funs = {
//sigblock
//sigsetmask
//siggetmask
//sigpending
//sigsuspend
//bsd_signal
//siginterrupt

Просмотреть файл

@ -140,7 +140,7 @@ var LibraryManager = {
var filename = libraries[i];
var src = read(filename);
try {
var processed = processMacros(preprocess(src));
var processed = processMacros(preprocess(src, filename));
eval(processed);
} catch(e) {
var details = [e, e.lineNumber ? 'line number: ' + e.lineNumber : '', (e.stack || "").toString().replace('Object.<anonymous>', filename)];

Просмотреть файл

@ -17,54 +17,69 @@ function processMacros(text) {
// Simple #if/else/endif preprocessing for a file. Checks if the
// ident checked is true in our global.
// Also handles #include x.js (similar to C #include <file>)
function preprocess(text) {
// Param filenameHint can be passed as a description to identify the file that is being processed, used
// to locate errors for reporting.
function preprocess(text, filenameHint) {
var lines = text.split('\n');
var ret = '';
var showStack = [];
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line[line.length-1] == '\r') {
line = line.substr(0, line.length-1); // Windows will have '\r' left over from splitting over '\r\n'
}
if (!line[0] || line[0] != '#') {
if (showStack.indexOf(false) == -1) {
ret += line + '\n';
try {
if (line[line.length-1] == '\r') {
line = line.substr(0, line.length-1); // Windows will have '\r' left over from splitting over '\r\n'
}
} else {
if (line[1] == 'i') {
if (line[2] == 'f') { // if
var parts = line.split(' ');
var ident = parts[1];
var op = parts[2];
var value = parts[3];
if (op) {
if (op === '==') {
showStack.push(ident in this && this[ident] == value);
} else if (op === '!=') {
showStack.push(!(ident in this && this[ident] == value));
} else {
error('unsupported preprecessor op ' + op);
}
} else {
if (ident[0] === '!') {
showStack.push(!(this[ident.substr(1)] > 0));
} else {
showStack.push(ident in this && this[ident] > 0);
}
}
} else if (line[2] == 'n') { // include
var included = read(line.substr(line.indexOf(' ')+1));
ret += '\n' + preprocess(included) + '\n'
if (!line[0] || line[0] != '#') {
if (showStack.indexOf(false) == -1) {
ret += line + '\n';
}
} else if (line[2] == 'l') { // else
assert(showStack.length > 0);
showStack.push(!showStack.pop());
} else if (line[2] == 'n') { // endif
assert(showStack.length > 0);
showStack.pop();
} else {
throw "Unclear preprocessor command: " + line;
if (line[1] == 'i') {
if (line[2] == 'f') { // if
var parts = line.split(' ');
var ident = parts[1];
var op = parts[2];
var value = parts[3];
if (op) {
if (op === '==') {
showStack.push(ident in this && this[ident] == value);
} else if (op === '!=') {
showStack.push(!(ident in this && this[ident] == value));
} else if (op === '<') {
showStack.push(ident in this && this[ident] < value);
} else if (op === '>') {
showStack.push(ident in this && this[ident] > value);
} else {
error('unsupported preprocessor op ' + op);
}
} else {
if (ident[0] === '!') {
showStack.push(!(this[ident.substr(1)] > 0));
} else {
showStack.push(ident in this && this[ident] > 0);
}
}
} else if (line[2] == 'n') { // include
var filename = line.substr(line.indexOf(' ')+1);
if (filename.indexOf('"') === 0) {
filename = filename.substr(1, filename.length - 2);
}
var included = read(filename);
ret += '\n' + preprocess(included, filename) + '\n'
}
} else if (line[2] == 'l') { // else
assert(showStack.length > 0);
showStack.push(!showStack.pop());
} else if (line[2] == 'n') { // endif
assert(showStack.length > 0);
showStack.pop();
} else {
throw "Unclear preprocessor command: " + line;
}
}
} catch(e) {
printErr('parseTools.js preprocessor error in ' + filenameHint + ':' + (i+1) + ': \"' + line + '\"!');
throw e;
}
}
assert(showStack.length == 0);

Просмотреть файл

@ -1,7 +1,11 @@
// === Auto-generated postamble setup entry stuff ===
#if USE_PTHREADS
if (memoryInitializer && !ENVIRONMENT_IS_PTHREAD) {
#else
if (memoryInitializer) {
#endif
if (typeof Module['locateFile'] === 'function') {
memoryInitializer = Module['locateFile'](memoryInitializer);
} else if (Module['memoryInitializerPrefixURL']) {
@ -191,6 +195,10 @@ function exit(status, implicit) {
Module.printErr('exit(' + status + ') called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)');
#endif
} else {
#if USE_PTHREADS
PThread.terminateAllThreads();
#endif
ABORT = true;
EXITSTATUS = status;
STACKTOP = initialStackTop;
@ -228,6 +236,9 @@ Module['exit'] = Module.exit = exit;
var abortDecorators = [];
function abort(what) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) console.error('Pthread aborting at ' + new Error().stack);
#endif
if (what !== undefined) {
Module.print(what);
Module.printErr(what);
@ -278,7 +289,11 @@ if (Module['noInitialRun']) {
Module["noExitRuntime"] = true;
#endif
#if USE_PTHREADS
if (!ENVIRONMENT_IS_PTHREAD) run();
#else
run();
#endif
// {{POST_RUN_ADDITIONS}}

Просмотреть файл

@ -1007,7 +1007,14 @@ var STATIC_BASE = 0, STATICTOP = 0, staticSealed = false; // static area
var STACK_BASE = 0, STACKTOP = 0, STACK_MAX = 0; // stack area
var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) staticSealed = true; // The static memory area has been initialized already in the main thread, pthreads skip this.
#endif
function enlargeMemory() {
#if USE_PTHREADS
abort('Cannot enlarge memory arrays, since compiling with pthreads support enabled (-s USE_PTHREADS=1).');
#else
#if ALLOW_MEMORY_GROWTH == 0
abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
#else
@ -1090,7 +1097,8 @@ function enlargeMemory() {
#endif
return true;
#endif
#endif // ALLOW_MEMORY_GROWTH
#endif // USE_PTHREADS
}
#if ALLOW_MEMORY_GROWTH
@ -1127,8 +1135,58 @@ if (totalMemory !== TOTAL_MEMORY) {
assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
'JS engine does not provide full typed array support');
var buffer = new ArrayBuffer(TOTAL_MEMORY);
var buffer;
#if USE_PTHREADS
if (typeof SharedArrayBuffer === 'undefined' || typeof Atomics === 'undefined') {
#if IN_TEST_HARNESS
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:8888/report_result?skipped:%20SharedArrayBuffer%20is%20not%20supported!');
xhr.send();
setTimeout(function() { window.close() }, 2000);
#endif
abort('Your browser does not support the SharedArrayBuffer and Atomics specification! Try running in Firefox Nightly.');
}
if (!ENVIRONMENT_IS_PTHREAD) buffer = new SharedArrayBuffer(TOTAL_MEMORY);
// Currently SharedArrayBuffer does not have a slice() operation, so polyfill it in.
// Adapted from https://github.com/ttaubert/node-arraybuffer-slice, (c) 2014 Tim Taubert <tim@timtaubert.de>
// arraybuffer-slice may be freely distributed under the MIT license.
(function (undefined) {
"use strict";
function clamp(val, length) {
val = (val|0) || 0;
if (val < 0) return Math.max(val + length, 0);
return Math.min(val, length);
}
if (!SharedArrayBuffer.prototype.slice) {
SharedArrayBuffer.prototype.slice = function (from, to) {
var length = this.byteLength;
var begin = clamp(from, length);
var end = length;
if (to !== undefined) end = clamp(to, length);
if (begin > end) return new ArrayBuffer(0);
var num = end - begin;
var target = new ArrayBuffer(num);
var targetArray = new Uint8Array(target);
var sourceArray = new SharedUint8Array(this, begin, num);
targetArray.set(sourceArray);
return target;
};
}
})();
HEAP8 = new SharedInt8Array(buffer);
HEAP16 = new SharedInt16Array(buffer);
HEAP32 = new SharedInt32Array(buffer);
HEAPU8 = new SharedUint8Array(buffer);
HEAPU16 = new SharedUint16Array(buffer);
HEAPU32 = new SharedUint32Array(buffer);
HEAPF32 = new SharedFloat32Array(buffer);
HEAPF64 = new SharedFloat64Array(buffer);
#else // USE_PTHREADS
buffer = new ArrayBuffer(TOTAL_MEMORY);
HEAP8 = new Int8Array(buffer);
HEAP16 = new Int16Array(buffer);
HEAP32 = new Int32Array(buffer);
@ -1137,6 +1195,7 @@ HEAPU16 = new Uint16Array(buffer);
HEAPU32 = new Uint32Array(buffer);
HEAPF32 = new Float32Array(buffer);
HEAPF64 = new Float64Array(buffer);
#endif // USE_PTHREADS
// Endianness check (note: assumes compiler arch was little-endian)
HEAP32[0] = 255;
@ -1182,7 +1241,14 @@ var __ATPOSTRUN__ = []; // functions called after the runtime has exited
var runtimeInitialized = false;
var runtimeExited = false;
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) runtimeInitialized = true; // The runtime is hosted in the main thread, and bits shared to pthreads via SharedArrayBuffer. No need to init again in pthread.
#endif
function preRun() {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
#endif
// compatibility - merge in anything from Module['preRun'] at this time
if (Module['preRun']) {
if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
@ -1194,21 +1260,33 @@ function preRun() {
}
function ensureInitRuntime() {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
#endif
if (runtimeInitialized) return;
runtimeInitialized = true;
callRuntimeCallbacks(__ATINIT__);
}
function preMain() {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
#endif
callRuntimeCallbacks(__ATMAIN__);
}
function exitRuntime() {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
#endif
callRuntimeCallbacks(__ATEXIT__);
runtimeExited = true;
}
function postRun() {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return; // PThreads reuse the runtime from the main thread.
#endif
// compatibility - merge in anything from Module['postRun'] at this time
if (Module['postRun']) {
if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
@ -1305,6 +1383,21 @@ Module['writeAsciiToMemory'] = writeAsciiToMemory;
{{{ unSign }}}
{{{ reSign }}}
#if USE_PTHREADS
// Atomics.exchange is not yet implemented in the spec, so polyfill that in via compareExchange in the meanwhile.
// TODO: Keep an eye out for the opportunity to remove this once Atomics.exchange is available.
if (typeof Atomics !== 'undefined' && !Atomics['exchange']) {
Atomics['exchange'] = function(heap, index, val) {
var oldVal, oldVal2;
do {
oldVal = Atomics['load'](heap, index);
oldVal2 = Atomics['compareExchange'](heap, index, oldVal, val);
} while(oldVal != oldVal2);
return oldVal;
}
}
#endif
// check for imul support, and also for correctness ( https://bugs.webkit.org/show_bug.cgi?id=126345 )
if (!Math['imul'] || Math['imul'](0xffffffff, 5) !== -5) Math['imul'] = function imul(a, b) {
var ah = a >>> 16;
@ -1501,5 +1594,48 @@ function lookupSymbol(ptr) { // for a pointer, print out all symbols that resolv
var memoryInitializer = null;
// === Body ===
#if USE_PTHREADS
#if PTHREAD_HINT_NUM_CORES < 0
if (!ENVIRONMENT_IS_PTHREAD) addOnPreRun(function() {
addRunDependency('pthreads_querycores');
var bg = document.createElement('div');
bg.style = "position: absolute; top: 0%; left: 0%; width: 100%; height: 100%; background-color: black; z-index:1001; -moz-opacity: 0.8; opacity:.80; filter: alpha(opacity=80);";
var div = document.createElement('div');
var default_num_cores = navigator.hardwareConcurrency || 4;
var hwConcurrency = navigator.hardwareConcurrency ? ("says " + navigator.hardwareConcurrency) : "is not available";
var html = '<div style="width: 100%; text-align:center;"> Thread setup</div> <br /> Number of logical cores: <input type="number" style="width: 50px;" value="'
+ default_num_cores + '" min="1" max="32" id="thread_setup_num_logical_cores"></input> <br /><span style="font-size: 75%;">(<span style="font-family: monospace;">navigator.hardwareConcurrency</span> '
+ hwConcurrency + ')</span> <br />';
#if PTHREAD_POOL_SIZE < 0
html += 'PThread pool size: <input type="number" style="width: 50px;" value="'
+ default_num_cores + '" min="1" max="32" id="thread_setup_pthread_pool_size"></input> <br />';
#endif
html += ' <br /> <input type="button" id="thread_setup_button_go" value="Go"></input>';
div.innerHTML = html;
div.style = 'position: absolute; top: 35%; left: 35%; width: 30%; height: 150px; padding: 16px; border: 16px solid gray; background-color: white; z-index:1002; overflow: auto;';
document.body.appendChild(bg);
document.body.appendChild(div);
var goButton = document.getElementById('thread_setup_button_go');
goButton.onclick = function() {
var num_logical_cores = parseInt(document.getElementById('thread_setup_num_logical_cores').value);
_emscripten_force_num_logical_cores(num_logical_cores);
#if PTHREAD_POOL_SIZE < 0
var pthread_pool_size = parseInt(document.getElementById('thread_setup_pthread_pool_size').value);
PThread.allocateUnusedWorkers(pthread_pool_size, function() { removeRunDependency('pthreads_querycores'); });
#else
removeRunDependency('pthreads_querycores');
#endif
document.body.removeChild(bg);
document.body.removeChild(div);
}
});
#endif
#endif
#if PTHREAD_POOL_SIZE > 0
// To work around https://bugzilla.mozilla.org/show_bug.cgi?id=1049079, warm up a worker pool before starting up the application.
if (!ENVIRONMENT_IS_PTHREAD) addOnPreRun(function() { addRunDependency('pthreads'); PThread.allocateUnusedWorkers({{{PTHREAD_POOL_SIZE}}}, function() { removeRunDependency('pthreads'); }); });
#endif
// === Body ===

94
src/pthread-main.js Normal file
Просмотреть файл

@ -0,0 +1,94 @@
// Pthread Web Worker startup routine:
// This is the entry point file that is loaded first by each Web Worker
// that executes pthreads on the Emscripten application.
// All pthreads share the same Emscripten HEAP as SharedArrayBuffer
// with the main execution thread.
var buffer;
var threadInfoStruct = 0; // Info area for this thread in Emscripten HEAP (shared). If zero, this worker is not currently hosting an executing pthread.
var selfThreadId = 0; // The ID of this thread. 0 if not hosting a pthread.
var tempDoublePtr = 0; // A temporary memory area for global float and double marshalling operations.
// Each thread has its own allocated stack space.
var STACK_BASE = 0;
var STACKTOP = 0;
var STACK_MAX = 0;
var ENVIRONMENT_IS_PTHREAD = true;
// Cannot use console.log or console.error in a web worker, since that would risk a browser deadlock! https://bugzilla.mozilla.org/show_bug.cgi?id=1049091
// Therefore implement custom logging facility for threads running in a worker, which queue the messages to main thread to print.
var Module = {};
function threadPrint() {
var text = Array.prototype.slice.call(arguments).join(' ');
postMessage({cmd: 'print', text: text, threadId: selfThreadId});
}
function threadPrintErr() {
var text = Array.prototype.slice.call(arguments).join(' ');
postMessage({cmd: 'printErr', text: text, threadId: selfThreadId});
}
Module['print'] = threadPrint;
Module['printErr'] = threadPrintErr;
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1049091
console = {
log: threadPrint,
error: threadPrintErr
};
this.onmessage = function(e) {
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
buffer = e.data.buffer;
tempDoublePtr = e.data.tempDoublePtr;
PthreadWorkerInit = e.data.PthreadWorkerInit;
importScripts(e.data.url);
FS.createStandardStreams();
postMessage({ cmd: 'loaded' });
} else if (e.data.cmd === 'run') { // This worker was idle, and now should start executing its pthread entry point.
threadInfoStruct = e.data.threadInfoStruct;
assert(threadInfoStruct);
selfThreadId = e.data.selfThreadId;
assert(selfThreadId);
// TODO: Emscripten runtime has these variables twice(!), once outside the asm.js module, and a second time inside the asm.js module.
// Review why that is? Can those get out of sync?
STACK_BASE = STACKTOP = e.data.stackBase;
STACK_MAX = STACK_BASE + e.data.stackSize;
assert(STACK_BASE != 0);
assert(STACK_MAX > STACK_BASE);
Runtime.establishStackSpace(e.data.stackBase, e.data.stackBase + e.data.stackSize);
var result = 0;
try {
// HACK: Some code in the wild has instead signatures of form 'void *ThreadMain()', which seems to be ok in native code.
// To emulate supporting both in test suites, use the following form. This is brittle!
if (typeof asm['dynCall_ii'] !== 'undefined') {
result = asm.dynCall_ii(e.data.start_routine, e.data.arg); // pthread entry points are always of signature 'void *ThreadMain(void *arg)'
} else {
result = asm.dynCall_i(e.data.start_routine); // as a hack, try signature 'i' as fallback.
}
} catch(e) {
if (e === 'Canceled!') {
PThread.threadCancel();
return;
} else {
Atomics.store(HEAPU32, (threadInfoStruct + 4 /*{{{ C_STRUCTS.pthread.threadExitCode }}}*/ ) >> 2, -2 /*A custom entry specific to Emscripten denoting that the thread crashed.*/);
Atomics.store(HEAPU32, (threadInfoStruct + 0 /*{{{ C_STRUCTS.pthread.threadStatus }}}*/ ) >> 2, 1); // Mark the thread as no longer running.
_emscripten_futex_wake(threadInfoStruct + 0 /*{{{ C_STRUCTS.pthread.threadStatus }}}*/, 0x7FFFFFFF/*INT_MAX*/); // wake all threads
throw e;
}
}
// The thread might have finished without calling pthread_exit(). If so, then perform the exit operation ourselves.
// (This is a no-op if explicit pthread_exit() had been called prior.)
PThread.threadExit(result);
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
if (threadInfoStruct && PThread.thisThreadCancelState == 0/*PTHREAD_CANCEL_ENABLE*/) {
PThread.threadCancel();
}
} else {
Module['printErr']('pthread-main.js received unknown command ' + e.data.cmd);
}
}

Просмотреть файл

@ -50,6 +50,9 @@ var RuntimeGenerator = {
// called, takes control of STATICTOP)
staticAlloc: function(size) {
if (ASSERTIONS) size = '(assert(!staticSealed),' + size + ')'; // static area must not be sealed
#if USE_PTHREADS
if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.staticAlloc is not available in pthreads!'; // This is because each worker has its own copy of STATICTOP, of which main thread is authoritative.
#endif
var ret = RuntimeGenerator.alloc(size, 'STATIC');
return ret;
},
@ -57,6 +60,9 @@ var RuntimeGenerator = {
// allocation on the top of memory, adjusted dynamically by sbrk
dynamicAlloc: function(size) {
if (ASSERTIONS) size = '(assert(DYNAMICTOP > 0),' + size + ')'; // dynamic area must be ready
#if USE_PTHREADS
if (typeof ENVIRONMENT_IS_PTHREAD !== 'undefined' && ENVIRONMENT_IS_PTHREAD) throw 'Runtime.dynamicAlloc is not available in pthreads!'; // This is because each worker has its own copy of DYNAMICTOP, of which main thread is authoritative.
#endif
var ret = RuntimeGenerator.alloc(size, 'DYNAMIC');
ret += '; if (DYNAMICTOP >= TOTAL_MEMORY) { var success = enlargeMemory(); if (!success) { DYNAMICTOP = ret; return 0; } }'
return ret;

Просмотреть файл

@ -520,4 +520,15 @@ var ORIGINAL_EXPORTED_FUNCTIONS = [];
// If you modify the headers, just clear your cache and emscripten libc should see
// the new values.
var IN_TEST_HARNESS = 0; // If true, the current build is performed for the Emscripten test harness.
var USE_PTHREADS = 0; // If true, enables support for pthreads.
var PTHREAD_POOL_SIZE = 0; // Specifies the number of web workers that are preallocated before runtime is initialized. If 0, workers are created on demand.
// Specifies the value returned by the function emscripten_num_logical_cores()
// if navigator.hardwareConcurrency is not supported. Pass in a negative number
// to show a popup dialog at startup so the user can configure this dynamically.
var PTHREAD_HINT_NUM_CORES = 4;
// Reserved: variables containing POINTER_MASKING.

Просмотреть файл

@ -36,7 +36,18 @@ for (var key in Module) {
// *** Environment setup code ***
var ENVIRONMENT_IS_WEB = typeof window === 'object';
var ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof require === 'function' && !ENVIRONMENT_IS_WEB;
// Three configurations we can be running in:
// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false)
// 2) We could be the application main() thread proxied to worker. (with Emscripten -s PROXY_TO_WORKER=1) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false)
// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true)
var ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
#if USE_PTHREADS
var ENVIRONMENT_IS_PTHREAD;
if (!ENVIRONMENT_IS_PTHREAD) ENVIRONMENT_IS_PTHREAD = false; // ENVIRONMENT_IS_PTHREAD=true will have been preset in pthread-main.js. Make it false in the main runtime thread.
var PthreadWorkerInit; // Collects together variables that are needed at initialization time for the web workers that host pthreads.
if (!ENVIRONMENT_IS_PTHREAD) PthreadWorkerInit = {};
var currentScriptUrl = ENVIRONMENT_IS_WORKER ? undefined : document.currentScript.src;
#endif
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
if (ENVIRONMENT_IS_NODE) {

Просмотреть файл

@ -15,6 +15,13 @@
]
}
},
{
"file": "libc/limits.h",
"defines": [
"INT_MAX"
],
"structs": {}
},
{
"file": "libc/utime.h",
"defines": [],
@ -154,6 +161,13 @@
]
}
},
{
"file": "libc/limits.h",
"defines": [
"PTHREAD_KEYS_MAX"
],
"structs": {}
},
{
"file": "libc/sys/utsname.h",
"defines": [],
@ -1379,5 +1393,168 @@
{ "angularAcceleration": [ "x", "y", "z", "w" ] }
]
}
},
{
"file": "emscripten/threading.h",
"structs": {},
"defines": [
"EM_PROXIED_FOPEN",
"EM_PROXIED_FGETS",
"EM_PROXIED_FPUTS",
"EM_PROXIED_FCLOSE",
"EM_PROXIED_OPENDIR",
"EM_PROXIED_CLOSEDIR",
"EM_PROXIED_TELLDIR",
"EM_PROXIED_SEEKDIR",
"EM_PROXIED_REWINDDIR",
"EM_PROXIED_READDIR_R",
"EM_PROXIED_READDIR",
"EM_PROXIED_UTIME",
"EM_PROXIED_UTIMES",
"EM_PROXIED_STAT",
"EM_PROXIED_LSTAT",
"EM_PROXIED_FSTAT",
"EM_PROXIED_MKNOD",
"EM_PROXIED_MKDIR",
"EM_PROXIED_MKFIFO",
"EM_PROXIED_CHMOD",
"EM_PROXIED_FCHMOD",
"EM_PROXIED_LCHMOD",
"EM_PROXIED_UMASK",
"EM_PROXIED_STATVFS",
"EM_PROXIED_FSTATVFS",
"EM_PROXIED_OPEN",
"EM_PROXIED_CREAT",
"EM_PROXIED_MKTEMP",
"EM_PROXIED_MKSTEMP",
"EM_PROXIED_MKDTEMP",
"EM_PROXIED_FCNTL",
"EM_PROXIED_POSIX_FALLOCATE",
"EM_PROXIED_POLL",
"EM_PROXIED_ACCESS",
"EM_PROXIED_CHDIR",
"EM_PROXIED_CHOWN",
"EM_PROXIED_CHROOT",
"EM_PROXIED_CLOSE",
"EM_PROXIED_DUP",
"EM_PROXIED_DUP2",
"EM_PROXIED_FCHOWN",
"EM_PROXIED_FCHDIR",
"EM_PROXIED_CTERMID",
"EM_PROXIED_CRYPT",
"EM_PROXIED_ENCRYPT",
"EM_PROXIED_FPATHCONF",
"EM_PROXIED_FSYNC",
"EM_PROXIED_TRUNCATE",
"EM_PROXIED_FTRUNCATE",
"EM_PROXIED_GETCWD",
"EM_PROXIED_ISATTY",
"EM_PROXIED_LCHOWN",
"EM_PROXIED_LINK",
"EM_PROXIED_LOCKF",
"EM_PROXIED_LSEEK",
"EM_PROXIED_PIPE",
"EM_PROXIED_PREAD",
"EM_PROXIED_READ",
"EM_PROXIED_RMDIR",
"EM_PROXIED_UNLINK",
"EM_PROXIED_TTYNAME",
"EM_PROXIED_TTYNAME_R",
"EM_PROXIED_SYMLINK",
"EM_PROXIED_READLINK",
"EM_PROXIED_PWRITE",
"EM_PROXIED_WRITE",
"EM_PROXIED_CONFSTR",
"EM_PROXIED_GETHOSTNAME",
"EM_PROXIED_GETLOGIN",
"EM_PROXIED_GETLOGIN_R",
"EM_PROXIED_SYSCONF",
"EM_PROXIED_SBRK",
"EM_PROXIED_CLEARERR",
"EM_PROXIED_FDOPEN",
"EM_PROXIED_FEOF",
"EM_PROXIED_FERROR",
"EM_PROXIED_FFLUSH",
"EM_PROXIED_FGETC",
"EM_PROXIED_GETCHAR",
"EM_PROXIED_FGETPOS",
"EM_PROXIED_GETS",
"EM_PROXIED_FILENO",
"EM_PROXIED_FPUTC",
"EM_PROXIED_PUTCHAR",
"EM_PROXIED_PUTS",
"EM_PROXIED_FREAD",
"EM_PROXIED_FREOPEN",
"EM_PROXIED_FSEEK",
"EM_PROXIED_FSETPOS",
"EM_PROXIED_FTELL",
"EM_PROXIED_FWRITE",
"EM_PROXIED_POPEN",
"EM_PROXIED_PCLOSE",
"EM_PROXIED_PERROR",
"EM_PROXIED_REMOVE",
"EM_PROXIED_RENAME",
"EM_PROXIED_REWIND",
"EM_PROXIED_TMPNAM",
"EM_PROXIED_TEMPNAM",
"EM_PROXIED_TMPFILE",
"EM_PROXIED_UNGETC",
"EM_PROXIED_FSCANF",
"EM_PROXIED_SCANF",
"EM_PROXIED_FPRINTF",
"EM_PROXIED_PRINTF",
"EM_PROXIED_DPRINTF",
"EM_PROXIED_MMAP",
"EM_PROXIED_MUNMAP",
"EM_PROXIED_ATEXIT",
"EM_PROXIED_GETENV",
"EM_PROXIED_CLEARENV",
"EM_PROXIED_SETENV",
"EM_PROXIED_UNSETENV",
"EM_PROXIED_PUTENV",
"EM_PROXIED_REALPATH",
"EM_PROXIED_TCGETATTR",
"EM_PROXIED_TCSETATTR",
"EM_PROXIED_TZSET",
"EM_PROXIED_SOCKET",
"EM_PROXIED_BIND",
"EM_PROXIED_SENDMSG",
"EM_PROXIED_RECVMSG",
"EM_PROXIED_SHUTDOWN",
"EM_PROXIED_IOCTL",
"EM_PROXIED_ACCEPT",
"EM_PROXIED_SELECT",
"EM_PROXIED_CONNECT",
"EM_PROXIED_LISTEN",
"EM_PROXIED_GETSOCKNAME",
"EM_PROXIED_GETPEERNAME",
"EM_PROXIED_SEND",
"EM_PROXIED_RECV",
"EM_PROXIED_SENDTO",
"EM_PROXIED_RECVFROM",
"EM_PROXIED_GETSOCKOPT",
"EM_PROXIED_PTHREAD_CREATE"
]
},
{
"file": "../lib/libc/musl/src/internal/pthread_impl.h",
"structs": {
"pthread": [
"threadStatus",
"threadExitCode",
"self",
"tsd",
"tsd_used",
"detached",
"stack",
"stack_size",
"attr",
"tid",
"pid",
"canceldisable",
"cancelasync"
]
},
"defines": []
}
]

Просмотреть файл

@ -0,0 +1,252 @@
#ifndef __emscripten_threading_h__
#define __emscripten_threading_h__
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
int emscripten_num_logical_cores();
// Configures the number of logical cores on the system. This can be called at startup
// to specify the number of cores emscripten_num_logical_cores() reports. The
// Emscripten system itself does not use this value internally anywhere, it is just
// a hint to help developers have a single access point 'emscripten_num_logical_cores()'
// to query the number of cores in the system.
void emscripten_force_num_logical_cores(int cores);
// Atomically stores the given value to the memory location, and returns the value that was there prior to the store.
uint8_t emscripten_atomic_exchange_u8(void/*uint8_t*/ *addr, uint8_t newVal);
uint16_t emscripten_atomic_exchange_u16(void/*uint16_t*/ *addr, uint16_t newVal);
uint32_t emscripten_atomic_exchange_u32(void/*uint32_t*/ *addr, uint32_t newVal);
uint64_t emscripten_atomic_exchange_u64(void/*uint64_t*/ *addr, uint64_t newVal); // Emulated with locks, very slow!!
// CAS returns the *old* value that was in the memory location before the operation took place.
// That is, if the return value when calling this function equals to 'oldVal', then the operation succeeded,
// otherwise it was ignored.
uint8_t emscripten_atomic_cas_u8(void/*uint8_t*/ *addr, uint8_t oldVal, uint8_t newVal);
uint16_t emscripten_atomic_cas_u16(void/*uint16_t*/ *addr, uint16_t oldVal, uint16_t newVal);
uint32_t emscripten_atomic_cas_u32(void/*uint32_t*/ *addr, uint32_t oldVal, uint32_t newVal);
uint64_t emscripten_atomic_cas_u64(void/*uint64_t*/ *addr, uint64_t oldVal, uint64_t newVal); // Emulated with locks, very slow!!
uint8_t emscripten_atomic_load_u8(const void/*uint8_t*/ *addr);
uint16_t emscripten_atomic_load_u16(const void/*uint16_t*/ *addr);
uint32_t emscripten_atomic_load_u32(const void/*uint32_t*/ *addr);
float emscripten_atomic_load_f32(const void/*float*/ *addr);
uint64_t emscripten_atomic_load_u64(const void/*uint64_t*/ *addr); // Emulated with locks, very slow!!
double emscripten_atomic_load_f64(const void/*double*/ *addr); // Emulated with locks, very slow!!
// Returns the value that was stored (i.e. 'val')
uint8_t emscripten_atomic_store_u8(void/*uint8_t*/ *addr, uint8_t val);
uint16_t emscripten_atomic_store_u16(void/*uint16_t*/ *addr, uint16_t val);
uint32_t emscripten_atomic_store_u32(void/*uint32_t*/ *addr, uint32_t val);
float emscripten_atomic_store_f32(void/*float*/ *addr, float val);
uint64_t emscripten_atomic_store_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
double emscripten_atomic_store_f64(void/*double*/ *addr, double val); // Emulated with locks, very slow!!
void emscripten_atomic_fence();
// Each of the functions below (add, sub, and, or, xor) returns the value that was stored to memory after the operation occurred.
uint8_t emscripten_atomic_add_u8(void/*uint8_t*/ *addr, uint8_t val);
uint16_t emscripten_atomic_add_u16(void/*uint16_t*/ *addr, uint16_t val);
uint32_t emscripten_atomic_add_u32(void/*uint32_t*/ *addr, uint32_t val);
uint64_t emscripten_atomic_add_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
uint8_t emscripten_atomic_sub_u8(void/*uint8_t*/ *addr, uint8_t val);
uint16_t emscripten_atomic_sub_u16(void/*uint16_t*/ *addr, uint16_t val);
uint32_t emscripten_atomic_sub_u32(void/*uint32_t*/ *addr, uint32_t val);
uint64_t emscripten_atomic_sub_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
uint8_t emscripten_atomic_and_u8(void/*uint8_t*/ *addr, uint8_t val);
uint16_t emscripten_atomic_and_u16(void/*uint16_t*/ *addr, uint16_t val);
uint32_t emscripten_atomic_and_u32(void/*uint32_t*/ *addr, uint32_t val);
uint64_t emscripten_atomic_and_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
uint8_t emscripten_atomic_or_u8(void/*uint8_t*/ *addr, uint8_t val);
uint16_t emscripten_atomic_or_u16(void/*uint16_t*/ *addr, uint16_t val);
uint32_t emscripten_atomic_or_u32(void/*uint32_t*/ *addr, uint32_t val);
uint64_t emscripten_atomic_or_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
uint8_t emscripten_atomic_xor_u8(void/*uint8_t*/ *addr, uint8_t val);
uint16_t emscripten_atomic_xor_u16(void/*uint16_t*/ *addr, uint16_t val);
uint32_t emscripten_atomic_xor_u32(void/*uint32_t*/ *addr, uint32_t val);
uint64_t emscripten_atomic_xor_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
int emscripten_futex_wait(void/*uint32_t*/ *addr, uint32_t val, double maxWaitMilliseconds);
int emscripten_futex_wake(void/*uint32_t*/ *addr, int count);
int emscripten_futex_wake_or_requeue(void/*uint32_t*/ *addr, int count, int cmpValue, void/*uint32_t*/ *addr2);
typedef union em_variant_val
{
int i;
float f;
double d;
void *vp;
char *cp;
} em_variant_val;
#define EM_QUEUED_CALL_MAX_ARGS 8
typedef struct em_queued_call
{
int function;
int operationDone;
em_variant_val args[EM_QUEUED_CALL_MAX_ARGS];
em_variant_val returnValue;
} em_queued_call;
void emscripten_sync_run_in_main_thread(em_queued_call *call);
void *emscripten_sync_run_in_main_thread_1(int function, void *arg1);
void *emscripten_sync_run_in_main_thread_2(int function, void *arg1, void *arg2);
void *emscripten_sync_run_in_main_thread_3(int function, void *arg1, void *arg2, void *arg3);
// Returns 1 if the current thread is the thread that hosts the Emscripten runtime.
int emscripten_is_main_runtime_thread(void);
// Returns 1 if the current thread is the main browser thread.
int emscripten_is_main_browser_thread(void);
void emscripten_main_thread_process_queued_calls();
#define EM_PROXIED_FOPEN 1
#define EM_PROXIED_FGETS 2
#define EM_PROXIED_FPUTS 3
#define EM_PROXIED_FCLOSE 4
#define EM_PROXIED_OPENDIR 5
#define EM_PROXIED_CLOSEDIR 6
#define EM_PROXIED_TELLDIR 7
#define EM_PROXIED_SEEKDIR 8
#define EM_PROXIED_REWINDDIR 9
#define EM_PROXIED_READDIR_R 10
#define EM_PROXIED_READDIR 11
#define EM_PROXIED_UTIME 12
#define EM_PROXIED_UTIMES 13
#define EM_PROXIED_STAT 14
#define EM_PROXIED_LSTAT 15
#define EM_PROXIED_FSTAT 16
#define EM_PROXIED_MKNOD 17
#define EM_PROXIED_MKDIR 18
#define EM_PROXIED_MKFIFO 19
#define EM_PROXIED_CHMOD 20
#define EM_PROXIED_FCHMOD 21
#define EM_PROXIED_LCHMOD 22
#define EM_PROXIED_UMASK 23
#define EM_PROXIED_STATVFS 24
#define EM_PROXIED_FSTATVFS 25
#define EM_PROXIED_OPEN 26
#define EM_PROXIED_CREAT 27
#define EM_PROXIED_MKTEMP 28
#define EM_PROXIED_MKSTEMP 29
#define EM_PROXIED_MKDTEMP 30
#define EM_PROXIED_FCNTL 31
#define EM_PROXIED_POSIX_FALLOCATE 32
#define EM_PROXIED_POLL 33
#define EM_PROXIED_ACCESS 34
#define EM_PROXIED_CHDIR 35
#define EM_PROXIED_CHOWN 36
#define EM_PROXIED_CHROOT 37
#define EM_PROXIED_CLOSE 38
#define EM_PROXIED_DUP 39
#define EM_PROXIED_DUP2 40
#define EM_PROXIED_FCHOWN 41
#define EM_PROXIED_FCHDIR 42
#define EM_PROXIED_CTERMID 43
#define EM_PROXIED_CRYPT 44
#define EM_PROXIED_ENCRYPT 45
#define EM_PROXIED_FPATHCONF 46
#define EM_PROXIED_FSYNC 47
#define EM_PROXIED_TRUNCATE 48
#define EM_PROXIED_FTRUNCATE 49
#define EM_PROXIED_GETCWD 50
#define EM_PROXIED_ISATTY 52
#define EM_PROXIED_LCHOWN 53
#define EM_PROXIED_LINK 54
#define EM_PROXIED_LOCKF 55
#define EM_PROXIED_LSEEK 56
#define EM_PROXIED_PIPE 57
#define EM_PROXIED_PREAD 58
#define EM_PROXIED_READ 59
#define EM_PROXIED_RMDIR 60
#define EM_PROXIED_UNLINK 61
#define EM_PROXIED_TTYNAME 62
#define EM_PROXIED_TTYNAME_R 63
#define EM_PROXIED_SYMLINK 64
#define EM_PROXIED_READLINK 65
#define EM_PROXIED_PWRITE 66
#define EM_PROXIED_WRITE 67
#define EM_PROXIED_CONFSTR 68
#define EM_PROXIED_GETHOSTNAME 69
#define EM_PROXIED_GETLOGIN 70
#define EM_PROXIED_GETLOGIN_R 71
#define EM_PROXIED_SYSCONF 72
#define EM_PROXIED_SBRK 73
#define EM_PROXIED_CLEARERR 74
#define EM_PROXIED_FDOPEN 75
#define EM_PROXIED_FEOF 76
#define EM_PROXIED_FERROR 77
#define EM_PROXIED_FFLUSH 78
#define EM_PROXIED_FGETC 79
#define EM_PROXIED_GETCHAR 80
#define EM_PROXIED_FGETPOS 81
#define EM_PROXIED_GETS 82
#define EM_PROXIED_FILENO 83
#define EM_PROXIED_FPUTC 84
#define EM_PROXIED_PUTCHAR 85
#define EM_PROXIED_PUTS 86
#define EM_PROXIED_FREAD 87
#define EM_PROXIED_FREOPEN 88
#define EM_PROXIED_FSEEK 89
#define EM_PROXIED_FSETPOS 90
#define EM_PROXIED_FTELL 91
#define EM_PROXIED_FWRITE 92
#define EM_PROXIED_POPEN 93
#define EM_PROXIED_PCLOSE 94
#define EM_PROXIED_PERROR 95
#define EM_PROXIED_REMOVE 96
#define EM_PROXIED_RENAME 97
#define EM_PROXIED_REWIND 98
#define EM_PROXIED_TMPNAM 99
#define EM_PROXIED_TEMPNAM 100
#define EM_PROXIED_TMPFILE 101
#define EM_PROXIED_UNGETC 102
#define EM_PROXIED_FSCANF 103
#define EM_PROXIED_SCANF 104
#define EM_PROXIED_FPRINTF 105
#define EM_PROXIED_PRINTF 106
#define EM_PROXIED_DPRINTF 107
#define EM_PROXIED_MMAP 108
#define EM_PROXIED_MUNMAP 109
#define EM_PROXIED_ATEXIT 110
#define EM_PROXIED_GETENV 111
#define EM_PROXIED_CLEARENV 112
#define EM_PROXIED_SETENV 113
#define EM_PROXIED_UNSETENV 114
#define EM_PROXIED_PUTENV 115
#define EM_PROXIED_REALPATH 116
#define EM_PROXIED_TCGETATTR 117
#define EM_PROXIED_TCSETATTR 118
#define EM_PROXIED_TZSET 119
#define EM_PROXIED_SOCKET 120
#define EM_PROXIED_BIND 121
#define EM_PROXIED_SENDMSG 122
#define EM_PROXIED_RECVMSG 123
#define EM_PROXIED_SHUTDOWN 124
#define EM_PROXIED_IOCTL 125
#define EM_PROXIED_ACCEPT 126
#define EM_PROXIED_SELECT 127
#define EM_PROXIED_CONNECT 128
#define EM_PROXIED_LISTEN 129
#define EM_PROXIED_GETSOCKNAME 130
#define EM_PROXIED_GETPEERNAME 131
#define EM_PROXIED_SEND 132
#define EM_PROXIED_RECV 133
#define EM_PROXIED_SENDTO 134
#define EM_PROXIED_RECVFROM 135
#define EM_PROXIED_GETSOCKOPT 136
#define EM_PROXIED_PTHREAD_CREATE 137
#ifdef __cplusplus
}
#endif
#endif

Просмотреть файл

@ -88,7 +88,13 @@ typedef struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
#endif
#if defined(__NEED_pthread_mutex_t) && !defined(__DEFINED_pthread_mutex_t)
#ifdef __EMSCRIPTEN__
// For mutex implementation in Emscripten, need to use an extra seventh control field
// to hold a temporary futex wait & wake location, designated as mutex->_m_addr.
typedef struct { union { int __i[7]; void *__p[7]; } __u; } pthread_mutex_t;
#else
typedef struct { union { int __i[6]; void *__p[6]; } __u; } pthread_mutex_t;
#endif
#define __DEFINED_pthread_mutex_t
#endif

Просмотреть файл

@ -203,11 +203,18 @@ struct __ptcb {
struct __ptcb *__next;
};
#ifdef __EMSCRIPTEN__
// For Emscripten, the cleanup stack is not implemented as a macro, since it's currently in the JS side.
typedef void (*cleanup_handler_routine)(void *arg);
void pthread_cleanup_push(cleanup_handler_routine routine, void *arg);
void pthread_cleanup_pop(int execute);
#else
void _pthread_cleanup_push(struct __ptcb *, void (*)(void *), void *);
void _pthread_cleanup_pop(struct __ptcb *, int);
#define pthread_cleanup_push(f, x) do { struct __ptcb __cb; _pthread_cleanup_push(&__cb, f, x);
#define pthread_cleanup_pop(r) _pthread_cleanup_pop(&__cb, (r)); } while(0)
#endif
#ifdef _GNU_SOURCE
struct cpu_set_t;

Просмотреть файл

@ -8,6 +8,13 @@
#define MORECORE_CANNOT_TRIM 1
/* XXX Emscripten Tracing API. This defines away the code if tracing is disabled. */
#include <emscripten/trace.h>
/* Make malloc() and free() threadsafe by securing the memory allocations with pthread mutexes. */
#if __EMSCRIPTEN_PTHREADS__
#define USE_LOCKS 1
#define USE_SPIN_LOCKS 0 // Ensure we use pthread_mutex_t.
#endif
#endif
@ -2017,6 +2024,7 @@ static void init_malloc_global_mutex() {
}
#else /* pthreads-based locks */
#define MLOCK_T pthread_mutex_t
#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk)
#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk)

Просмотреть файл

@ -2,6 +2,7 @@
#define _INTERNAL_ATOMIC_H
#include <stdint.h>
#include <emscripten/threading.h>
static inline int a_ctz_l(unsigned long x)
{
@ -37,58 +38,66 @@ static inline void a_or_64(volatile uint64_t *p, uint64_t v)
static inline void a_store_l(volatile void *p, long x)
{
*(long*)p = x;
emscripten_atomic_store_u32((void*)p, x);
}
static inline void a_or_l(volatile void *p, long v)
{
*(long*)p |= v;
emscripten_atomic_or_u32((void*)p, v);
}
static inline void *a_cas_p(volatile void *p, void *t, void *s)
{
if (*(long*)p == t)
*(long*)p = s;
return t;
return (void*)emscripten_atomic_cas_u32(p, (uint32_t)t, (uint32_t)s);
}
static inline long a_cas_l(volatile void *p, long t, long s)
{
if (*(long*)p == t)
*(long*)p = s;
return t;
return emscripten_atomic_cas_u32(p, t, s);
}
static inline int a_cas(volatile int *p, int t, int s)
{
if (*p == t)
*p = s;
return t;
return emscripten_atomic_cas_u32(p, t, s);
}
static inline void a_or(volatile void *p, int v)
{
*(int*)p |= v;
emscripten_atomic_or_u32((void*)p, v);
}
static inline void a_and(volatile void *p, int v)
{
*(int*)p &= v;
emscripten_atomic_and_u32((void*)p, v);
}
static inline int a_swap(volatile int *x, int v)
{
int old;
do {
old = emscripten_atomic_load_u32(x);
} while(emscripten_atomic_cas_u32(x, old, v) != old);
return old;
}
static inline int a_fetch_add(volatile int *x, int v)
{
return emscripten_atomic_add_u32(x, v);
}
static inline void a_inc(volatile int *x)
{
++*x;
emscripten_atomic_add_u32((void*)x, 1);
}
static inline void a_dec(volatile int *x)
{
--*x;
emscripten_atomic_sub_u32((void*)x, 1);
}
static inline void a_store(volatile int *p, int x)
{
*p = x;
emscripten_atomic_store_u32((void*)p, x);
}
static inline void a_spin()

Просмотреть файл

@ -0,0 +1,154 @@
#ifndef _PTHREAD_IMPL_H
#define _PTHREAD_IMPL_H
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include "libc.h"
#include "syscall.h"
#include "atomic.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/threading.h>
#else
#include "futex.h"
#endif
#define pthread __pthread
struct pthread {
// XXX Emscripten: Need some custom thread control structures.
#ifdef __EMSCRIPTEN__
// Note: The specific order of these fields is important, since these are accessed
// by direct pointer arithmetic in pthread-main.js.
int threadStatus; // 0: thread not exited, 1: exited.
int threadExitCode; // Thread exit code.
int tempDoublePtr[3]; // Temporary memory area for double operations in runtime.
#endif
struct pthread *self;
void **dtv, *unused1, *unused2;
uintptr_t sysinfo;
uintptr_t canary;
pid_t tid, pid;
int tsd_used, errno_val, *errno_ptr;
volatile int cancel, canceldisable, cancelasync;
int detached;
unsigned char *map_base;
size_t map_size;
void *stack;
size_t stack_size;
void *start_arg;
void *(*start)(void *);
void *result;
struct __ptcb *cancelbuf;
void **tsd;
pthread_attr_t attr;
volatile int dead;
struct {
void **head;
long off;
void *pending;
} robust_list;
int unblock_cancel;
int timer_id;
locale_t locale;
int killlock[2];
int exitlock[2];
int startlock[2];
unsigned long sigmask[_NSIG/8/sizeof(long)];
};
struct __timer {
int timerid;
pthread_t thread;
};
#define __SU (sizeof(size_t)/sizeof(int))
#define _a_stacksize __u.__s[0]
#define _a_guardsize __u.__s[1]
#define _a_stackaddr __u.__s[2]
#define _a_detach __u.__i[3*__SU+0]
#define _a_sched __u.__i[3*__SU+1]
#define _a_policy __u.__i[3*__SU+2]
#define _a_prio __u.__i[3*__SU+3]
#define _m_type __u.__i[0]
#define _m_lock __u.__i[1]
#define _m_waiters __u.__i[2]
#define _m_prev __u.__p[3]
#define _m_next __u.__p[4]
#define _m_count __u.__i[5]
#ifdef __EMSCRIPTEN__
#define _m_addr __u.__i[6]
#endif
#define _c_mutex __u.__p[0]
#define _c_seq __u.__i[2]
#define _c_waiters __u.__i[3]
#define _c_clock __u.__i[4]
#define _c_lock __u.__i[5]
#define _c_lockwait __u.__i[6]
#define _c_waiters2 __u.__i[7]
#define _c_destroy __u.__i[8]
#define _rw_lock __u.__i[0]
#define _rw_waiters __u.__i[1]
#ifdef __EMSCRIPTEN__
// XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, so use an extra field
// _rw_wr_owner to record which thread owns the write lock in order to avoid hangs.
// Points to the pthread that currently has the write lock.
#define _rw_wr_owner __u.__i[2]
#endif
#define _b_lock __u.__i[0]
#define _b_waiters __u.__i[1]
#define _b_limit __u.__i[2]
#define _b_count __u.__i[3]
#define _b_waiters2 __u.__i[4]
#define _b_inst __u.__p[3]
#ifndef __EMSCRIPTEN__ // XXX Not currently used for Emscripten.
#include "pthread_arch.h"
#endif
#define SIGTIMER 32
#define SIGCANCEL 33
#define SIGSYNCCALL 34
#define SIGALL_SET ((sigset_t *)(const unsigned long long [2]){ -1,-1 })
#define SIGPT_SET \
((sigset_t *)(const unsigned long [_NSIG/8/sizeof(long)]){ \
[sizeof(long)==4] = 3UL<<(32*(sizeof(long)>4)) })
#define SIGTIMER_SET \
((sigset_t *)(const unsigned long [_NSIG/8/sizeof(long)]){ \
0x80000000 })
pthread_t __pthread_self_init(void);
int __clone(int (*)(void *), void *, int, void *, ...);
int __set_thread_area(void *);
int __libc_sigaction(int, const struct sigaction *, struct sigaction *);
int __libc_sigprocmask(int, const sigset_t *, sigset_t *);
void __lock(volatile int *);
void __unmapself(void *, size_t);
int __timedwait(volatile int *, int, clockid_t, const struct timespec *, void (*)(void *), void *, int);
void __wait(volatile int *, volatile int *, int, int);
#ifdef __EMSCRIPTEN__
#define __wake(addr, cnt, priv) emscripten_futex_wake((void*)addr, (cnt)<0?INT_MAX:(cnt))
#else
#define __wake(addr, cnt, priv) \
__syscall(SYS_futex, addr, FUTEX_WAKE, (cnt)<0?INT_MAX:(cnt))
#endif
void __acquire_ptc();
void __release_ptc();
void __inhibit_ptc();
void __block_all_sigs(void *);
void __block_app_sigs(void *);
void __restore_sigs(void *);
#define DEFAULT_STACK_SIZE 81920
#define DEFAULT_GUARD_SIZE PAGE_SIZE
#endif

Просмотреть файл

@ -0,0 +1,84 @@
#include <pthread.h>
#include <time.h>
#include <errno.h>
#ifdef __EMSCRIPTEN__
#include <math.h>
#include <emscripten/threading.h>
#include <emscripten/emscripten.h>
#include "pthread_impl.h"
#else
#include "futex.h"
#endif
#include "syscall.h"
#ifdef __EMSCRIPTEN__
double _pthread_msecs_until(const struct timespec *restrict at);
int _pthread_isduecanceled(struct pthread *pthread_ptr);
#endif
static int do_wait(volatile int *addr, int val,
clockid_t clk, const struct timespec *at, int priv)
{
int r;
struct timespec to, *top=0;
if (at) {
if (at->tv_nsec >= 1000000000UL) return EINVAL;
if (clock_gettime(clk, &to)) return EINVAL;
to.tv_sec = at->tv_sec - to.tv_sec;
if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) {
to.tv_sec--;
to.tv_nsec += 1000000000;
}
if (to.tv_sec < 0) return ETIMEDOUT;
top = &to;
}
#ifdef __EMSCRIPTEN__
if (1 || pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) {
do {
if (_pthread_isduecanceled(pthread_self())) {
// Emscripten-specific return value: The wait was canceled by user calling
// pthread_cancel() for this thread, and the caller needs to cooperatively
// cancel execution.
return ECANCELED;
}
// Must wait in slices in case this thread is cancelled in between.
double waitMsecs = at ? _pthread_msecs_until(at) : INFINITY;
if (waitMsecs <= 0) {
r = ETIMEDOUT;
break;
}
if (waitMsecs > 100) waitMsecs = 100;
r = -emscripten_futex_wait((void*)addr, val, waitMsecs);
// Assist other threads by executing proxied operations that are effectively singlethreaded.
if (emscripten_is_main_runtime_thread()) emscripten_main_thread_process_queued_calls();
} while(r == ETIMEDOUT);
} else {
// Can wait in one go.
double waitMsecs = at ? _pthread_msecs_until(at) : INFINITY;
r = -emscripten_futex_wait((void*)addr, val, waitMsecs);
}
#else
r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top);
#endif
if (r == EINTR || r == EINVAL || r == ETIMEDOUT) return r;
return 0;
}
int __timedwait(volatile int *addr, int val,
clockid_t clk, const struct timespec *at,
void (*cleanup)(void *), void *arg, int priv)
{
int r, cs;
if (!cleanup) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
pthread_cleanup_push(cleanup, arg);
r = do_wait(addr, val, clk, at, priv);
pthread_cleanup_pop(0);
if (!cleanup) pthread_setcancelstate(cs, 0);
return r;
}

Просмотреть файл

@ -0,0 +1,39 @@
#ifdef __EMSCRIPTEN__
#include <math.h>
#endif
#include "pthread_impl.h"
int _pthread_isduecanceled(struct pthread *pthread_ptr);
void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
{
int spins=10000;
if (priv) priv = 128; priv=0;
while (spins--) {
if (*addr==val) a_spin();
else return;
}
if (waiters) a_inc(waiters);
while (*addr==val) {
#ifdef __EMSCRIPTEN__
if (pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) {
// Must wait in slices in case this thread is cancelled in between.
int e;
do {
if (_pthread_isduecanceled(pthread_self())) {
if (waiters) a_dec(waiters);
return;
}
e = emscripten_futex_wait((void*)addr, val, 100);
} while(e == -ETIMEDOUT);
} else {
// Can wait in one go.
emscripten_futex_wait((void*)addr, val, INFINITY);
}
#else
__syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0);
#endif
}
if (waiters) a_dec(waiters);
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_attr_destroy(pthread_attr_t *a)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,102 @@
#include "pthread_impl.h"
int pthread_attr_getdetachstate(const pthread_attr_t *a, int *state)
{
*state = a->_a_detach;
return 0;
}
int pthread_attr_getguardsize(const pthread_attr_t *restrict a, size_t *restrict size)
{
*size = a->_a_guardsize + DEFAULT_GUARD_SIZE;
return 0;
}
int pthread_attr_getinheritsched(const pthread_attr_t *restrict a, int *restrict inherit)
{
*inherit = a->_a_sched;
return 0;
}
int pthread_attr_getschedparam(const pthread_attr_t *restrict a, struct sched_param *restrict param)
{
param->sched_priority = a->_a_prio;
return 0;
}
int pthread_attr_getschedpolicy(const pthread_attr_t *restrict a, int *restrict policy)
{
*policy = a->_a_policy;
return 0;
}
int pthread_attr_getscope(const pthread_attr_t *restrict a, int *restrict scope)
{
*scope = PTHREAD_SCOPE_SYSTEM;
return 0;
}
int pthread_attr_getstack(const pthread_attr_t *restrict a, void **restrict addr, size_t *restrict size)
{
/// XXX musl is not standard-conforming? It should not report EINVAL if _a_stackaddr is zero, and it should
/// report EINVAL if a is null: http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getstack.html
if (!a) return EINVAL;
// if (!a->_a_stackaddr)
// return EINVAL;
*size = a->_a_stacksize + DEFAULT_STACK_SIZE;
*addr = (void *)(a->_a_stackaddr - *size);
return 0;
}
int pthread_attr_getstacksize(const pthread_attr_t *restrict a, size_t *restrict size)
{
*size = a->_a_stacksize + DEFAULT_STACK_SIZE;
return 0;
}
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict a, int *restrict pshared)
{
*pshared = !!a->__attr;
return 0;
}
int pthread_condattr_getclock(const pthread_condattr_t *restrict a, clockid_t *restrict clk)
{
*clk = a->__attr & 0x7fffffff;
return 0;
}
int pthread_condattr_getpshared(const pthread_condattr_t *restrict a, int *restrict pshared)
{
*pshared = a->__attr>>31;
return 0;
}
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict a, int *restrict protocol)
{
*protocol = PTHREAD_PRIO_NONE;
return 0;
}
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict a, int *restrict pshared)
{
*pshared = a->__attr>>31;
return 0;
}
int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict a, int *restrict robust)
{
*robust = a->__attr / 4U % 2;
return 0;
}
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict a, int *restrict type)
{
*type = a->__attr & 3;
return 0;
}
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict a, int *restrict pshared)
{
*pshared = a->__attr[0];
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_attr_init(pthread_attr_t *a)
{
*a = (pthread_attr_t){0};
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_attr_setdetachstate(pthread_attr_t *a, int state)
{
if (state > 1U) return EINVAL;
a->_a_detach = state;
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_attr_setguardsize(pthread_attr_t *a, size_t size)
{
if (size > SIZE_MAX/8) return EINVAL;
a->_a_guardsize = size - DEFAULT_GUARD_SIZE;
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_attr_setinheritsched(pthread_attr_t *a, int inherit)
{
if (inherit > 1U) return EINVAL;
a->_a_sched = inherit;
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_attr_setschedparam(pthread_attr_t *restrict a, const struct sched_param *restrict param)
{
a->_a_prio = param->sched_priority;
return 0;
}

Просмотреть файл

@ -0,0 +1,10 @@
#include "pthread_impl.h"
int pthread_attr_setschedpolicy(pthread_attr_t *a, int policy)
{
#ifdef __EMSCRIPTEN__ // XXX Emscripten: upstream this fix to musl.
if (policy < SCHED_OTHER || (policy & ~SCHED_RESET_ON_FORK) > SCHED_IDLE) return EINVAL;
#endif
a->_a_policy = policy;
return 0;
}

Просмотреть файл

@ -0,0 +1,13 @@
#include "pthread_impl.h"
int pthread_attr_setscope(pthread_attr_t *a, int scope)
{
switch (scope) {
case PTHREAD_SCOPE_SYSTEM:
return 0;
case PTHREAD_SCOPE_PROCESS:
return ENOTSUP;
default:
return EINVAL;
}
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_attr_setstack(pthread_attr_t *a, void *addr, size_t size)
{
if (size-PTHREAD_STACK_MIN > SIZE_MAX/4) return EINVAL;
a->_a_stackaddr = (size_t)addr + size;
a->_a_stacksize = size - DEFAULT_STACK_SIZE;
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_attr_setstacksize(pthread_attr_t *a, size_t size)
{
if (size-PTHREAD_STACK_MIN > SIZE_MAX/4) return EINVAL;
a->_a_stackaddr = 0;
a->_a_stacksize = size - DEFAULT_STACK_SIZE;
return 0;
}

Просмотреть файл

@ -0,0 +1,18 @@
#include "pthread_impl.h"
void __vm_lock(int), __vm_unlock(void);
int pthread_barrier_destroy(pthread_barrier_t *b)
{
if (b->_b_limit < 0) {
if (b->_b_lock) {
int v;
a_or(&b->_b_lock, INT_MIN);
while ((v = b->_b_lock) & INT_MAX)
__wait(&b->_b_lock, 0, v, 0);
}
__vm_lock(-1);
__vm_unlock();
}
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_barrier_init(pthread_barrier_t *restrict b, const pthread_barrierattr_t *restrict a, unsigned count)
{
if (count-1 > INT_MAX-1) return EINVAL;
*b = (pthread_barrier_t){ ._b_limit = count-1 | (a?a->__attr:0) };
return 0;
}

Просмотреть файл

@ -0,0 +1,122 @@
#ifdef __EMSCRIPTEN__
#include <math.h>
#endif
#include "pthread_impl.h"
void __vm_lock_impl(int);
void __vm_unlock_impl(void);
static int pshared_barrier_wait(pthread_barrier_t *b)
{
int limit = (b->_b_limit & INT_MAX) + 1;
int ret = 0;
int v, w;
if (limit==1) return PTHREAD_BARRIER_SERIAL_THREAD;
while ((v=a_cas(&b->_b_lock, 0, limit)))
__wait(&b->_b_lock, &b->_b_waiters, v, 0);
/* Wait for <limit> threads to get to the barrier */
if (++b->_b_count == limit) {
a_store(&b->_b_count, 0);
ret = PTHREAD_BARRIER_SERIAL_THREAD;
if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
} else {
a_store(&b->_b_lock, 0);
if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
while ((v=b->_b_count)>0)
__wait(&b->_b_count, &b->_b_waiters2, v, 0);
}
__vm_lock_impl(+1);
/* Ensure all threads have a vm lock before proceeding */
if (a_fetch_add(&b->_b_count, -1)==1-limit) {
a_store(&b->_b_count, 0);
if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
} else {
while ((v=b->_b_count))
__wait(&b->_b_count, &b->_b_waiters2, v, 0);
}
/* Perform a recursive unlock suitable for self-sync'd destruction */
do {
v = b->_b_lock;
w = b->_b_waiters;
} while (a_cas(&b->_b_lock, v, v==INT_MIN+1 ? 0 : v-1) != v);
/* Wake a thread waiting to reuse or destroy the barrier */
if (v==INT_MIN+1 || (v==1 && w))
__wake(&b->_b_lock, 1, 0);
__vm_unlock_impl();
return ret;
}
struct instance
{
int count;
int last;
int waiters;
int finished;
};
int pthread_barrier_wait(pthread_barrier_t *b)
{
int limit = b->_b_limit;
struct instance *inst;
/* Trivial case: count was set at 1 */
if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
/* Process-shared barriers require a separate, inefficient wait */
if (limit < 0) return pshared_barrier_wait(b);
/* Otherwise we need a lock on the barrier object */
while (a_swap(&b->_b_lock, 1))
__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
inst = b->_b_inst;
/* First thread to enter the barrier becomes the "instance owner" */
if (!inst) {
struct instance new_inst = { 0 };
int spins = 10000;
b->_b_inst = inst = &new_inst;
a_store(&b->_b_lock, 0);
if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
while (spins-- && !inst->finished)
a_spin();
a_inc(&inst->finished);
while (inst->finished == 1) {
#ifdef __EMSCRIPTEN__
emscripten_futex_wait(&inst->finished, 1, INFINITY);
#else
__syscall(SYS_futex, &inst->finished, FUTEX_WAIT,1,0);
#endif
}
return PTHREAD_BARRIER_SERIAL_THREAD;
}
/* Last thread to enter the barrier wakes all non-instance-owners */
if (++inst->count == limit) {
b->_b_inst = 0;
a_store(&b->_b_lock, 0);
if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
a_store(&inst->last, 1);
if (inst->waiters)
__wake(&inst->last, -1, 1);
} else {
a_store(&b->_b_lock, 0);
if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
__wait(&inst->last, &inst->waiters, 0, 1);
}
/* Last thread to exit the barrier wakes the instance owner */
if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
__wake(&inst->finished, 1, 1);
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_barrierattr_destroy(pthread_barrierattr_t *a)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_barrierattr_init(pthread_barrierattr_t *a)
{
*a = (pthread_barrierattr_t){0};
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_barrierattr_setpshared(pthread_barrierattr_t *a, int pshared)
{
a->__attr = pshared ? INT_MIN : 0;
return 0;
}

Просмотреть файл

@ -0,0 +1,55 @@
#include "pthread_impl.h"
int pthread_cond_broadcast(pthread_cond_t *c)
{
pthread_mutex_t *m;
if (!c->_c_waiters) return 0;
a_inc(&c->_c_seq);
#ifdef __EMSCRIPTEN__
// XXX Emscripten: TODO: This is suboptimal but works naively correctly for now. The Emscripten-specific code path below
// has a bug and does not work for some reason. Figure it out and remove this code block.
__wake(&c->_c_seq, -1, 0);
return 0;
#endif
/* If cond var is process-shared, simply wake all waiters. */
if (c->_c_mutex == (void *)-1) {
__wake(&c->_c_seq, -1, 0);
return 0;
}
/* Block waiters from returning so we can use the mutex. */
while (a_swap(&c->_c_lock, 1))
__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
if (!c->_c_waiters)
goto out;
m = c->_c_mutex;
/* Move waiter count to the mutex */
a_fetch_add(&m->_m_waiters, c->_c_waiters2);
c->_c_waiters2 = 0;
#ifdef __EMSCRIPTEN__
int futexResult;
do {
// XXX Emscripten: Bug, this does not work correctly.
futexResult = emscripten_futex_wake_or_requeue(&c->_c_seq, !m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
c->_c_seq, &m->_m_lock);
} while(futexResult == -EAGAIN);
#else
/* Perform the futex requeue, waking one waiter unless we know
* that the calling thread holds the mutex. */
__syscall(SYS_futex, &c->_c_seq, FUTEX_REQUEUE,
!m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
INT_MAX, &m->_m_lock);
#endif
out:
a_store(&c->_c_lock, 0);
if (c->_c_lockwait) __wake(&c->_c_lock, 1, 0);
return 0;
}

Просмотреть файл

@ -0,0 +1,13 @@
#include "pthread_impl.h"
int pthread_cond_destroy(pthread_cond_t *c)
{
int priv = c->_c_mutex != (void *)-1;
int cnt;
c->_c_destroy = 1;
if (c->_c_waiters)
__wake(&c->_c_seq, -1, priv);
while ((cnt = c->_c_waiters))
__wait(&c->_c_waiters, 0, cnt, priv);
return 0;
}

Просмотреть файл

@ -0,0 +1,11 @@
#include "pthread_impl.h"
int pthread_cond_init(pthread_cond_t *restrict c, const pthread_condattr_t *restrict a)
{
*c = (pthread_cond_t){0};
if (a) {
c->_c_clock = a->__attr & 0x7fffffff;
if (a->__attr>>31) c->_c_mutex = (void *)-1;
}
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_cond_signal(pthread_cond_t *c)
{
if (!c->_c_waiters) return 0;
a_inc(&c->_c_seq);
if (c->_c_waiters) __wake(&c->_c_seq, 1, 0);
return 0;
}

Просмотреть файл

@ -0,0 +1,82 @@
#include "pthread_impl.h"
struct cm {
pthread_cond_t *c;
pthread_mutex_t *m;
};
static void unwait(pthread_cond_t *c, pthread_mutex_t *m)
{
/* Removing a waiter is non-trivial if we could be using requeue
* based broadcast signals, due to mutex access issues, etc. */
if (c->_c_mutex == (void *)-1) {
a_dec(&c->_c_waiters);
if (c->_c_destroy) __wake(&c->_c_waiters, 1, 0);
return;
}
while (a_swap(&c->_c_lock, 1))
__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
if (c->_c_waiters2) c->_c_waiters2--;
else a_dec(&m->_m_waiters);
a_store(&c->_c_lock, 0);
if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
a_dec(&c->_c_waiters);
if (c->_c_destroy) __wake(&c->_c_waiters, 1, 1);
}
static void cleanup(void *p)
{
struct cm *cm = p;
unwait(cm->c, cm->m);
pthread_mutex_lock(cm->m);
}
int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
{
struct cm cm = { .c=c, .m=m };
int r, e=0, seq;
if (m->_m_type && (m->_m_lock&INT_MAX) != pthread_self()->tid)
return EPERM;
if (ts && ts->tv_nsec >= 1000000000UL)
return EINVAL;
pthread_testcancel();
a_inc(&c->_c_waiters);
if (c->_c_mutex != (void *)-1) {
c->_c_mutex = m;
while (a_swap(&c->_c_lock, 1))
__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
c->_c_waiters2++;
a_store(&c->_c_lock, 0);
if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
}
seq = c->_c_seq;
pthread_mutex_unlock(m);
do {
e = __timedwait(&c->_c_seq, seq, c->_c_clock, ts, cleanup, &cm, 0);
}
while (c->_c_seq == seq && (!e || e==EINTR));
if (e == EINTR) e = 0;
unwait(c, m);
if ((r=pthread_mutex_lock(m))) return r;
#ifdef __EMSCRIPTEN__
pthread_testcancel();
#endif
return e;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m)
{
return pthread_cond_timedwait(c, m, 0);
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_condattr_destroy(pthread_condattr_t *a)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_condattr_init(pthread_condattr_t *a)
{
*a = (pthread_condattr_t){0};
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clk)
{
if (clk < 0 || clk-2U < 2) return EINVAL;
a->__attr &= 0x80000000;
a->__attr |= clk;
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_condattr_setpshared(pthread_condattr_t *a, int pshared)
{
if (pshared > 1U) return EINVAL;
a->__attr &= 0x7fffffff;
a->__attr |= pshared<<31;
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include <pthread.h>
int (pthread_equal)(pthread_t a, pthread_t b)
{
return a==b;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
void *pthread_getspecific(pthread_key_t k)
{
struct pthread *self = pthread_self();
return self->tsd[k];
}

Просмотреть файл

@ -0,0 +1,59 @@
#include "pthread_impl.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
const size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;
void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 };
static void (*keys[PTHREAD_KEYS_MAX])(void *);
static void nodtor(void *dummy)
{
}
int pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
{
unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX;
unsigned j = i;
#ifndef __EMSCRIPTEN__ // XXX Emscripten does not need specific initialization for threads, the runtime has initialized everything prior to running.
__pthread_self_init();
#endif
if (!dtor) dtor = nodtor;
do {
if (!a_cas_p(keys+j, 0, (void *)dtor)) {
*k = j;
return 0;
}
} while ((j=(j+1)%PTHREAD_KEYS_MAX) != i);
return EAGAIN;
}
int pthread_key_delete(pthread_key_t k)
{
keys[k] = 0;
return 0;
}
#ifdef __EMSCRIPTEN__
void EMSCRIPTEN_KEEPALIVE __pthread_tsd_run_dtors()
#else
void __pthread_tsd_run_dtors()
#endif
{
pthread_t self = pthread_self();
int i, j, not_finished = self->tsd_used;
for (j=0; not_finished && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
not_finished = 0;
for (i=0; i<PTHREAD_KEYS_MAX; i++) {
if (self->tsd[i] && keys[i]) {
void *tmp = self->tsd[i];
self->tsd[i] = 0;
keys[i](tmp);
not_finished = 1;
}
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
#include "pthread_impl.h"
int pthread_mutex_consistent(pthread_mutex_t *m)
{
if (m->_m_type < 8) return EINVAL;
if ((m->_m_lock & 0x3fffffff) != pthread_self()->tid)
return EPERM;
m->_m_type -= 8;
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict m, int *restrict ceiling)
{
return EINVAL;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_mutex_init(pthread_mutex_t *restrict m, const pthread_mutexattr_t *restrict a)
{
*m = (pthread_mutex_t){0};
if (a) m->_m_type = a->__attr & 7;
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_mutex_lock(pthread_mutex_t *m)
{
if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
return 0;
return pthread_mutex_timedlock(m, 0);
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_mutex_setprioceiling(pthread_mutex_t *restrict m, int ceiling, int *restrict old)
{
return EINVAL;
}

Просмотреть файл

@ -0,0 +1,24 @@
#include "pthread_impl.h"
int pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
{
int r, t;
if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
return 0;
while ((r=pthread_mutex_trylock(m)) == EBUSY) {
if (!(r=m->_m_lock) || (r&0x40000000)) continue;
if ((m->_m_type&3) == PTHREAD_MUTEX_ERRORCHECK
&& (r&0x1fffffff) == pthread_self()->tid)
return EDEADLK;
a_inc(&m->_m_waiters);
t = r | 0x80000000;
a_cas(&m->_m_lock, r, t);
r = __timedwait(&m->_m_lock, t, CLOCK_REALTIME, at, 0, 0, 0);
a_dec(&m->_m_waiters);
if (r && r != EINTR) break;
}
return r;
}

Просмотреть файл

@ -0,0 +1,54 @@
#include "pthread_impl.h"
int pthread_mutex_trylock(pthread_mutex_t *m)
{
int tid, old, own;
pthread_t self;
if (m->_m_type == PTHREAD_MUTEX_NORMAL)
return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY;
self = pthread_self();
tid = self->tid;
if (m->_m_type >= 4) {
#ifndef __EMSCRIPTEN__ // XXX Emscripten does not have a concept of multiple processes or kernel space, so robust mutex lists don't need to register to kernel.
if (!self->robust_list.off)
__syscall(SYS_set_robust_list,
&self->robust_list, 3*sizeof(long));
#endif
self->robust_list.off = (char*)&m->_m_lock-(char *)&m->_m_next;
self->robust_list.pending = &m->_m_next;
}
old = m->_m_lock;
own = old & 0x7fffffff;
if (own == tid && (m->_m_type&3) == PTHREAD_MUTEX_RECURSIVE) {
if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
m->_m_count++;
return 0;
}
if ((own && !(own & 0x40000000)) || a_cas(&m->_m_lock, old, tid)!=old)
return EBUSY;
if (m->_m_type < 4) return 0;
if (m->_m_type >= 8) {
m->_m_lock = 0;
return ENOTRECOVERABLE;
}
m->_m_next = self->robust_list.head;
m->_m_prev = &self->robust_list.head;
if (self->robust_list.head)
self->robust_list.head[-1] = &m->_m_next;
self->robust_list.head = &m->_m_next;
self->robust_list.pending = 0;
if (own) {
m->_m_count = 0;
m->_m_type += 8;
return EOWNERDEAD;
}
return 0;
}

Просмотреть файл

@ -0,0 +1,37 @@
#include "pthread_impl.h"
void __vm_lock_impl(int);
void __vm_unlock_impl(void);
int pthread_mutex_unlock(pthread_mutex_t *m)
{
pthread_t self;
int waiters = m->_m_waiters;
int cont;
int robust = 0;
if (m->_m_type != PTHREAD_MUTEX_NORMAL) {
if (!m->_m_lock)
return EPERM;
self = pthread_self();
if ((m->_m_lock&0x1fffffff) != self->tid)
return EPERM;
if ((m->_m_type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
return m->_m_count--, 0;
if (m->_m_type >= 4) {
robust = 1;
self->robust_list.pending = &m->_m_next;
*(void **)m->_m_prev = m->_m_next;
if (m->_m_next) ((void **)m->_m_next)[-1] = m->_m_prev;
__vm_lock_impl(+1);
}
}
cont = a_swap(&m->_m_lock, 0);
if (robust) {
self->robust_list.pending = 0;
__vm_unlock_impl();
}
if (waiters || cont<0)
__wake(&m->_m_lock, 1, 0);
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_mutexattr_init(pthread_mutexattr_t *a)
{
*a = (pthread_mutexattr_t){0};
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int protocol)
{
if (protocol) return ENOTSUP;
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int pshared)
{
if (pshared > 1U) return EINVAL;
a->__attr &= 0x7fffffff;
a->__attr |= pshared<<31;
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_mutexattr_setrobust(pthread_mutexattr_t *a, int robust)
{
if (robust > 1U) return EINVAL;
a->__attr &= ~4;
a->__attr |= robust*4;
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type)
{
if ((unsigned)type > 2) return EINVAL;
a->__attr = (a->__attr & ~3) | type;
return 0;
}

Просмотреть файл

@ -0,0 +1,36 @@
#include "pthread_impl.h"
static void undo(void *control)
{
a_store(control, 0);
__wake(control, 1, 0);
}
int pthread_once(pthread_once_t *control, void (*init)(void))
{
static int waiters;
/* Return immediately if init finished before */
if (*control == 2) return 0;
/* Try to enter initializing state. Three possibilities:
* 0 - we're the first or the other cancelled; run init
* 1 - another thread is running init; wait
* 2 - another thread finished running init; just return */
for (;;) switch (a_cas(control, 0, 1)) {
case 0:
pthread_cleanup_push(undo, control);
init();
pthread_cleanup_pop(0);
a_store(control, 2);
if (waiters) __wake(control, -1, 0);
return 0;
case 1:
__wait(control, &waiters, 1, 0);
continue;
case 2:
return 0;
}
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_rwlock_destroy(pthread_rwlock_t *rw)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,9 @@
#include "pthread_impl.h"
int pthread_rwlock_init(pthread_rwlock_t *restrict rw, const pthread_rwlockattr_t *restrict a)
{
*rw = (pthread_rwlock_t){0};
if (a) {
}
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_rwlock_rdlock(pthread_rwlock_t *rw)
{
return pthread_rwlock_timedrdlock(rw, 0);
}

Просмотреть файл

@ -0,0 +1,16 @@
#include "pthread_impl.h"
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
{
int r, t;
while ((r=pthread_rwlock_tryrdlock(rw))==EBUSY) {
if (!(r=rw->_rw_lock) || (r&0x7fffffff)!=0x7fffffff) continue;
t = r | 0x80000000;
a_inc(&rw->_rw_waiters);
a_cas(&rw->_rw_lock, r, t);
r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, 0, 0, 0);
a_dec(&rw->_rw_waiters);
if (r && r != EINTR) return r;
}
return r;
}

Просмотреть файл

@ -0,0 +1,26 @@
#include "pthread_impl.h"
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
{
#ifdef __EMSCRIPTEN__
/// XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, which we do here to avoid hangs.
/// If attempting to lock the write lock that we already own, error out.
if (rw->_rw_wr_owner == pthread_self()) return EDEADLK;
#endif
int r, t;
while ((r=pthread_rwlock_trywrlock(rw))==EBUSY) {
if (!(r=rw->_rw_lock)) continue;
t = r | 0x80000000;
a_inc(&rw->_rw_waiters);
a_cas(&rw->_rw_lock, r, t);
r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, 0, 0, 0);
a_dec(&rw->_rw_waiters);
if (r && r != EINTR) return r;
}
#ifdef __EMSCRIPTEN__
/// XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, which we do here to avoid hangs.
/// Mark this thread as the owner of this write lock.
rw->_rw_wr_owner = pthread_self();
#endif
return r;
}

Просмотреть файл

@ -0,0 +1,13 @@
#include "pthread_impl.h"
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw)
{
int val, cnt;
do {
val = rw->_rw_lock;
cnt = val & 0x7fffffff;
if (cnt == 0x7fffffff) return EBUSY;
if (cnt == 0x7ffffffe) return EAGAIN;
} while (a_cas(&rw->_rw_lock, val, val+1) != val);
return 0;
}

Просмотреть файл

@ -0,0 +1,12 @@
#include "pthread_impl.h"
int pthread_rwlock_trywrlock(pthread_rwlock_t *rw)
{
if (a_cas(&rw->_rw_lock, 0, 0x7fffffff)) return EBUSY;
#ifdef __EMSCRIPTEN__
/// XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, which we do here to avoid hangs.
/// Mark this thread to own the write lock, to ignore multiple attempts to lock.
rw->_rw_wr_owner = pthread_self();
#endif
return 0;
}

Просмотреть файл

@ -0,0 +1,24 @@
#include "pthread_impl.h"
int pthread_rwlock_unlock(pthread_rwlock_t *rw)
{
int val, cnt, waiters, new;
#ifdef __EMSCRIPTEN__
/// XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, which we do here to avoid hangs.
/// Mark this thread to not own the write lock anymore.
if (rw->_rw_wr_owner == pthread_self()) rw->_rw_wr_owner = 0;
#endif
do {
val = rw->_rw_lock;
cnt = val & 0x7fffffff;
waiters = rw->_rw_waiters;
new = (cnt == 0x7fffffff || cnt == 1) ? 0 : val-1;
} while (a_cas(&rw->_rw_lock, val, new) != val);
if (!new && (waiters || val<0))
__wake(&rw->_rw_lock, cnt, 0);
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_rwlock_wrlock(pthread_rwlock_t *rw)
{
return pthread_rwlock_timedwrlock(rw, 0);
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_rwlockattr_init(pthread_rwlockattr_t *a)
{
*a = (pthread_rwlockattr_t){0};
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include "pthread_impl.h"
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int pshared)
{
if (pshared > 1U) return EINVAL;
a->__attr[0] = pshared;
return 0;
}

Просмотреть файл

@ -0,0 +1,11 @@
#include "pthread_impl.h"
int pthread_setcanceltype(int new, int *old)
{
struct pthread *self = pthread_self();
if (new > 1U) return EINVAL;
if (old) *old = self->cancelasync;
self->cancelasync = new;
if (new) pthread_testcancel();
return 0;
}

Просмотреть файл

@ -0,0 +1,12 @@
#include "pthread_impl.h"
int pthread_setspecific(pthread_key_t k, const void *x)
{
struct pthread *self = pthread_self();
/* Avoid unnecessary COW */
if (self->tsd[k] != x) {
self->tsd[k] = (void *)x;
self->tsd_used = 1;
}
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_spin_destroy(pthread_spinlock_t *s)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_spin_init(pthread_spinlock_t *s, int shared)
{
return *s = 0;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_spin_lock(pthread_spinlock_t *s)
{
while (a_swap(s, 1)) a_spin();
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include "pthread_impl.h"
int pthread_spin_trylock(pthread_spinlock_t *s)
{
return -a_swap(s, 1) & EBUSY;
}

Просмотреть файл

@ -0,0 +1,7 @@
#include "pthread_impl.h"
int pthread_spin_unlock(pthread_spinlock_t *s)
{
a_store(s, 0);
return 0;
}

Просмотреть файл

@ -0,0 +1,6 @@
#include <semaphore.h>
int sem_destroy(sem_t *sem)
{
return 0;
}

Просмотреть файл

@ -0,0 +1,8 @@
#include <semaphore.h>
int sem_getvalue(sem_t *restrict sem, int *restrict valp)
{
int val = sem->__val[0];
*valp = val < 0 ? 0 : val;
return 0;
}

Просмотреть файл

@ -0,0 +1,14 @@
#include <semaphore.h>
#include <limits.h>
#include <errno.h>
int sem_init(sem_t *sem, int pshared, unsigned value)
{
if (value > SEM_VALUE_MAX) {
errno = EINVAL;
return -1;
}
sem->__val[0] = value;
sem->__val[1] = 0;
return 0;
}

Просмотреть файл

@ -0,0 +1,174 @@
#include <semaphore.h>
#include <sys/mman.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <pthread.h>
#include "libc.h"
char *__shm_mapname(const char *, char *);
static struct {
ino_t ino;
sem_t *sem;
int refcnt;
} *semtab;
static int lock[2];
#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
sem_t *sem_open(const char *name, int flags, ...)
{
va_list ap;
mode_t mode;
unsigned value;
int fd, i, e, slot, first=1, cnt, cs;
sem_t newsem;
void *map;
char tmp[64];
struct timespec ts;
struct stat st;
char buf[NAME_MAX+10];
if (!(name = __shm_mapname(name, buf)))
return SEM_FAILED;
LOCK(lock);
/* Allocate table if we don't have one yet */
if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
UNLOCK(lock);
return SEM_FAILED;
}
/* Reserve a slot in case this semaphore is not mapped yet;
* this is necessary because there is no way to handle
* failures after creation of the file. */
slot = -1;
for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
cnt += semtab[i].refcnt;
if (!semtab[i].sem && slot < 0) slot = i;
}
/* Avoid possibility of overflow later */
if (cnt == INT_MAX || slot < 0) {
errno = EMFILE;
UNLOCK(lock);
return SEM_FAILED;
}
/* Dummy pointer to make a reservation */
semtab[slot].sem = (sem_t *)-1;
UNLOCK(lock);
flags &= (O_CREAT|O_EXCL);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
/* Early failure check for exclusive open; otherwise the case
* where the semaphore already exists is expensive. */
if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
errno = EEXIST;
goto fail;
}
for (;;) {
/* If exclusive mode is not requested, try opening an
* existing file first and fall back to creation. */
if (flags != (O_CREAT|O_EXCL)) {
fd = open(name, FLAGS);
if (fd >= 0) {
if (fstat(fd, &st) < 0 ||
(map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
close(fd);
goto fail;
}
close(fd);
break;
}
if (errno != ENOENT)
goto fail;
}
if (!(flags & O_CREAT))
goto fail;
if (first) {
first = 0;
va_start(ap, flags);
mode = va_arg(ap, mode_t) & 0666;
value = va_arg(ap, unsigned);
va_end(ap);
if (value > SEM_VALUE_MAX) {
errno = EINVAL;
goto fail;
}
sem_init(&newsem, 1, value);
}
/* Create a temp file with the new semaphore contents
* and attempt to atomically link it as the new name */
clock_gettime(CLOCK_REALTIME, &ts);
snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
if (fd < 0) {
if (errno == EEXIST) continue;
goto fail;
}
if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
(map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
close(fd);
unlink(tmp);
goto fail;
}
close(fd);
e = link(tmp, name) ? errno : 0;
unlink(tmp);
if (!e) break;
/* Failure is only fatal when doing an exclusive open;
* otherwise, next iteration will try to open the
* existing file. */
if (e != EEXIST || flags == (O_CREAT|O_EXCL))
goto fail;
}
/* See if the newly mapped semaphore is already mapped. If
* so, unmap the new mapping and use the existing one. Otherwise,
* add it to the table of mapped semaphores. */
LOCK(lock);
for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
if (i<SEM_NSEMS_MAX) {
munmap(map, sizeof(sem_t));
semtab[slot].sem = 0;
slot = i;
map = semtab[i].sem;
}
semtab[slot].refcnt++;
semtab[slot].sem = map;
semtab[slot].ino = st.st_ino;
UNLOCK(lock);
pthread_setcancelstate(cs, 0);
return map;
fail:
pthread_setcancelstate(cs, 0);
LOCK(lock);
semtab[slot].sem = 0;
UNLOCK(lock);
return SEM_FAILED;
}
int sem_close(sem_t *sem)
{
int i;
LOCK(lock);
for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
if (!--semtab[i].refcnt) {
semtab[i].sem = 0;
semtab[i].ino = 0;
}
UNLOCK(lock);
munmap(sem, sizeof *sem);
return 0;
}

Просмотреть файл

@ -0,0 +1,17 @@
#include <semaphore.h>
#include "pthread_impl.h"
int sem_post(sem_t *sem)
{
int val, waiters;
do {
val = sem->__val[0];
waiters = sem->__val[1];
if (val == SEM_VALUE_MAX) {
errno = EOVERFLOW;
return -1;
}
} while (a_cas(sem->__val, val, val+1+(val<0)) != val);
if (val<0 || waiters) __wake(sem->__val, 1, 0);
return 0;
}

Просмотреть файл

@ -0,0 +1,26 @@
#include <semaphore.h>
#include "pthread_impl.h"
static void cleanup(void *p)
{
a_dec(p);
}
int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict at)
{
while (sem_trywait(sem)) {
int r;
a_inc(sem->__val+1);
a_cas(sem->__val, 0, -1);
r = __timedwait(sem->__val, -1, CLOCK_REALTIME, at, cleanup, sem->__val+1, 0);
#ifdef __EMSCRIPTEN__
if (r == ECANCELED) r = EINTR;
#endif
a_dec(sem->__val+1);
if (r) {
errno = r;
return -1;
}
}
return 0;
}

Просмотреть файл

@ -0,0 +1,13 @@
#include <semaphore.h>
#include "pthread_impl.h"
int sem_trywait(sem_t *sem)
{
int val;
while ((val=sem->__val[0]) > 0) {
int new = val-1-(val==1 && sem->__val[1]);
if (a_cas(sem->__val, val, new)==val) return 0;
}
errno = EAGAIN;
return -1;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше