D: Implement GCC emutls in druntime
* libdruntime/Makefile.am: Add emutls and gthread files. * libdruntime/Makefile.in: Regenerate. * libdruntime/gcc/emutls.d: New file. Implement GC-compatible emutls. * libdruntime/gcc/gthread.d: New file. * libdruntime/gcc/sections/elf_shared.d: Integrate emutls support. * testsuite/libphobos.allocations/tls_gc_integration.d: New test for TLS. From-SVN: r270568
This commit is contained in:
parent
7da021f080
commit
9168f22057
@ -1,3 +1,12 @@
|
||||
2019-04-25 Johannes Pfau <johannespfau@gmail.com>
|
||||
|
||||
* libdruntime/Makefile.am: Add emutls and gthread files.
|
||||
* libdruntime/Makefile.in: Regenerate.
|
||||
* libdruntime/gcc/emutls.d: New file. Implement GC-compatible emutls.
|
||||
* libdruntime/gcc/gthread.d: New file.
|
||||
* libdruntime/gcc/sections/elf_shared.d: Integrate emutls support.
|
||||
* testsuite/libphobos.allocations/tls_gc_integration.d: New test for TLS.
|
||||
|
||||
2019-04-25 Iain Buclaw <ibuclaw@gdcproject.org>
|
||||
|
||||
* testsuite/Makefile.am: Set PWD_COMMAND.
|
||||
|
@ -161,7 +161,8 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
|
||||
core/sync/config.d core/sync/exception.d core/sync/mutex.d \
|
||||
core/sync/rwmutex.d core/sync/semaphore.d core/thread.d core/time.d \
|
||||
core/vararg.d gcc/attribute.d gcc/backtrace.d gcc/builtins.d gcc/deh.d \
|
||||
gcc/sections/android.d gcc/sections/elf_shared.d gcc/sections/osx.d \
|
||||
gcc/emutls.d gcc/gthread.d gcc/sections/android.d \
|
||||
gcc/sections/elf_shared.d gcc/sections/osx.d \
|
||||
gcc/sections/package.d gcc/sections/win32.d gcc/sections/win64.d \
|
||||
gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
|
||||
gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
|
||||
|
@ -203,10 +203,10 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
|
||||
core/sync/exception.lo core/sync/mutex.lo core/sync/rwmutex.lo \
|
||||
core/sync/semaphore.lo core/thread.lo core/time.lo \
|
||||
core/vararg.lo gcc/attribute.lo gcc/backtrace.lo \
|
||||
gcc/builtins.lo gcc/deh.lo gcc/sections/android.lo \
|
||||
gcc/sections/elf_shared.lo gcc/sections/osx.lo \
|
||||
gcc/sections/package.lo gcc/sections/win32.lo \
|
||||
gcc/sections/win64.lo gcc/unwind/arm.lo \
|
||||
gcc/builtins.lo gcc/deh.lo gcc/emutls.lo gcc/gthread.lo \
|
||||
gcc/sections/android.lo gcc/sections/elf_shared.lo \
|
||||
gcc/sections/osx.lo gcc/sections/package.lo \
|
||||
gcc/sections/win32.lo gcc/sections/win64.lo gcc/unwind/arm.lo \
|
||||
gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
|
||||
gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
|
||||
object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
|
||||
@ -757,7 +757,8 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
|
||||
core/sync/config.d core/sync/exception.d core/sync/mutex.d \
|
||||
core/sync/rwmutex.d core/sync/semaphore.d core/thread.d core/time.d \
|
||||
core/vararg.d gcc/attribute.d gcc/backtrace.d gcc/builtins.d gcc/deh.d \
|
||||
gcc/sections/android.d gcc/sections/elf_shared.d gcc/sections/osx.d \
|
||||
gcc/emutls.d gcc/gthread.d gcc/sections/android.d \
|
||||
gcc/sections/elf_shared.d gcc/sections/osx.d \
|
||||
gcc/sections/package.d gcc/sections/win32.d gcc/sections/win64.d \
|
||||
gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
|
||||
gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
|
||||
@ -1104,6 +1105,8 @@ gcc/attribute.lo: gcc/$(am__dirstamp)
|
||||
gcc/backtrace.lo: gcc/$(am__dirstamp)
|
||||
gcc/builtins.lo: gcc/$(am__dirstamp)
|
||||
gcc/deh.lo: gcc/$(am__dirstamp)
|
||||
gcc/emutls.lo: gcc/$(am__dirstamp)
|
||||
gcc/gthread.lo: gcc/$(am__dirstamp)
|
||||
gcc/sections/$(am__dirstamp):
|
||||
@$(MKDIR_P) gcc/sections
|
||||
@: > gcc/sections/$(am__dirstamp)
|
||||
|
316
libphobos/libdruntime/gcc/emutls.d
Normal file
316
libphobos/libdruntime/gcc/emutls.d
Normal file
@ -0,0 +1,316 @@
|
||||
// GNU D Compiler emulated TLS routines.
|
||||
// Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
|
||||
// Under Section 7 of GPL version 3, you are granted additional
|
||||
// permissions described in the GCC Runtime Library Exception, version
|
||||
// 3.1, as published by the Free Software Foundation.
|
||||
|
||||
// You should have received a copy of the GNU General Public License and
|
||||
// a copy of the GCC Runtime Library Exception along with this program;
|
||||
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This code is based on the libgcc emutls.c emulated TLS support.
|
||||
|
||||
module gcc.emutls;
|
||||
|
||||
import core.atomic, core.stdc.stdlib, core.stdc.string, core.sync.mutex;
|
||||
import rt.util.container.array, rt.util.container.hashtab;
|
||||
import core.internal.traits : classInstanceAlignment;
|
||||
import gcc.builtins, gcc.gthread;
|
||||
|
||||
version (GNU_EMUTLS): private:
|
||||
|
||||
alias word = __builtin_machine_uint;
|
||||
alias pointer = __builtin_pointer_uint;
|
||||
alias TlsArray = Array!(void**);
|
||||
|
||||
/*
|
||||
* TLS control data emitted by GCC for every TLS variable.
|
||||
*/
|
||||
struct __emutls_object
|
||||
{
|
||||
word size;
|
||||
word align_;
|
||||
union
|
||||
{
|
||||
pointer offset;
|
||||
void* ptr;
|
||||
}
|
||||
|
||||
ubyte* templ;
|
||||
}
|
||||
|
||||
// Per-thread key to obtain the per-thread TLS variable array
|
||||
__gshared __gthread_key_t emutlsKey;
|
||||
// Largest, currently assigned TLS variable offset
|
||||
__gshared pointer emutlsMaxOffset = 0;
|
||||
// Contains the size of the TLS variables (for GC)
|
||||
__gshared Array!word emutlsSizes;
|
||||
// Contains the TLS variable array for single-threaded apps
|
||||
__gshared TlsArray singleArray;
|
||||
// List of all currently alive TlsArrays (for GC)
|
||||
__gshared HashTab!(TlsArray*, TlsArray*) emutlsArrays;
|
||||
|
||||
// emutlsMutex Mutex + @nogc handling
|
||||
enum mutexAlign = classInstanceAlignment!Mutex;
|
||||
enum mutexClassInstanceSize = __traits(classInstanceSize, Mutex);
|
||||
__gshared align(mutexAlign) void[mutexClassInstanceSize] _emutlsMutex;
|
||||
|
||||
@property Mutex emutlsMutex() nothrow @nogc
|
||||
{
|
||||
return cast(Mutex) _emutlsMutex.ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global (de)initialization functions
|
||||
*/
|
||||
extern (C) void _d_emutls_init() nothrow @nogc
|
||||
{
|
||||
memcpy(_emutlsMutex.ptr, typeid(Mutex).initializer.ptr, _emutlsMutex.length);
|
||||
(cast(Mutex) _emutlsMutex.ptr).__ctor();
|
||||
|
||||
if (__gthread_key_create(&emutlsKey, &emutlsDestroyThread) != 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
__gshared __gthread_once_t initOnce = GTHREAD_ONCE_INIT;
|
||||
|
||||
/*
|
||||
* emutls main entrypoint, called by GCC for each TLS variable access.
|
||||
*/
|
||||
extern (C) void* __emutls_get_address(shared __emutls_object* obj) nothrow @nogc
|
||||
{
|
||||
pointer offset;
|
||||
if (__gthread_active_p())
|
||||
{
|
||||
// Obtain the offset index into the TLS array (same for all-threads)
|
||||
// for requested var. If it is unset, obtain a new offset index.
|
||||
offset = atomicLoad!(MemoryOrder.acq, pointer)(obj.offset);
|
||||
if (__builtin_expect(offset == 0, 0))
|
||||
{
|
||||
__gthread_once(&initOnce, &_d_emutls_init);
|
||||
emutlsMutex.lock_nothrow();
|
||||
|
||||
offset = obj.offset;
|
||||
if (offset == 0)
|
||||
{
|
||||
offset = ++emutlsMaxOffset;
|
||||
|
||||
emutlsSizes.ensureLength(offset);
|
||||
// Note: it's important that we copy any data from obj and
|
||||
// do not keep an reference to obj itself: If a library is
|
||||
// unloaded, its tls variables are not removed from the arrays
|
||||
// and the GC will still scan these. If we then try to reference
|
||||
// a pointer to the data segment of an unloaded library, this
|
||||
// will crash.
|
||||
emutlsSizes[offset - 1] = obj.size;
|
||||
|
||||
atomicStore!(MemoryOrder.rel, pointer)(obj.offset, offset);
|
||||
}
|
||||
emutlsMutex.unlock_nothrow();
|
||||
}
|
||||
}
|
||||
// For single-threaded systems, don't synchronize
|
||||
else
|
||||
{
|
||||
if (__builtin_expect(obj.offset == 0, 0))
|
||||
{
|
||||
offset = ++emutlsMaxOffset;
|
||||
|
||||
emutlsSizes.ensureLength(offset);
|
||||
emutlsSizes[offset - 1] = obj.size;
|
||||
|
||||
obj.offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
TlsArray* arr;
|
||||
if (__gthread_active_p())
|
||||
arr = cast(TlsArray*) __gthread_getspecific(emutlsKey);
|
||||
else
|
||||
arr = &singleArray;
|
||||
|
||||
// This will always be false for singleArray
|
||||
if (__builtin_expect(arr == null, 0))
|
||||
{
|
||||
arr = mallocTlsArray(offset);
|
||||
__gthread_setspecific(emutlsKey, arr);
|
||||
emutlsMutex.lock_nothrow();
|
||||
emutlsArrays[arr] = arr;
|
||||
emutlsMutex.unlock_nothrow();
|
||||
}
|
||||
// Check if we have to grow the per-thread array
|
||||
else if (__builtin_expect(offset > arr.length, 0))
|
||||
{
|
||||
(*arr).ensureLength(offset);
|
||||
}
|
||||
|
||||
// Offset 0 is used as a not-initialized marker above. In the
|
||||
// TLS array, we start at 0.
|
||||
auto index = offset - 1;
|
||||
|
||||
// Get the per-thread pointer from the TLS array
|
||||
void** ret = (*arr)[index];
|
||||
if (__builtin_expect(ret == null, 0))
|
||||
{
|
||||
// Initial access, have to allocate the storage
|
||||
ret = emutlsAlloc(obj);
|
||||
(*arr)[index] = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 1:1 copy from libgcc emutls.c
|
||||
extern (C) void __emutls_register_common(__emutls_object* obj, word size, word align_, ubyte* templ) nothrow @nogc
|
||||
{
|
||||
if (obj.size < size)
|
||||
{
|
||||
obj.size = size;
|
||||
obj.templ = null;
|
||||
}
|
||||
if (obj.align_ < align_)
|
||||
obj.align_ = align_;
|
||||
if (templ && size == obj.size)
|
||||
obj.templ = templ;
|
||||
}
|
||||
|
||||
// 1:1 copy from libgcc emutls.c
|
||||
void** emutlsAlloc(shared __emutls_object* obj) nothrow @nogc
|
||||
{
|
||||
void* ptr;
|
||||
void* ret;
|
||||
enum pointerSize = (void*).sizeof;
|
||||
|
||||
/* We could use here posix_memalign if available and adjust
|
||||
emutls_destroy accordingly. */
|
||||
if ((cast() obj).align_ <= pointerSize)
|
||||
{
|
||||
ptr = malloc((cast() obj).size + pointerSize);
|
||||
if (ptr == null)
|
||||
abort();
|
||||
(cast(void**) ptr)[0] = ptr;
|
||||
ret = ptr + pointerSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = malloc(obj.size + pointerSize + obj.align_ - 1);
|
||||
if (ptr == null)
|
||||
abort();
|
||||
ret = cast(void*)((cast(pointer)(ptr + pointerSize + obj.align_ - 1)) & ~cast(
|
||||
pointer)(obj.align_ - 1));
|
||||
(cast(void**) ret)[-1] = ptr;
|
||||
}
|
||||
|
||||
if (obj.templ)
|
||||
memcpy(ret, cast(ubyte*) obj.templ, cast() obj.size);
|
||||
else
|
||||
memset(ret, 0, cast() obj.size);
|
||||
|
||||
return cast(void**) ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a thread has finished, remove the TLS array from the GC
|
||||
* scan list emutlsArrays, free all allocated TLS variables and
|
||||
* finally free the array.
|
||||
*/
|
||||
extern (C) void emutlsDestroyThread(void* ptr) nothrow @nogc
|
||||
{
|
||||
auto arr = cast(TlsArray*) ptr;
|
||||
emutlsMutex.lock_nothrow();
|
||||
emutlsArrays.remove(arr);
|
||||
emutlsMutex.unlock_nothrow();
|
||||
|
||||
foreach (entry; *arr)
|
||||
{
|
||||
if (entry)
|
||||
free(entry[-1]);
|
||||
}
|
||||
|
||||
free(arr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new TLS array, set length according to offset.
|
||||
*/
|
||||
TlsArray* mallocTlsArray(pointer offset = 0) nothrow @nogc
|
||||
{
|
||||
static assert(TlsArray.alignof == (void*).alignof);
|
||||
void[] data = malloc(TlsArray.sizeof)[0 .. TlsArray.sizeof];
|
||||
if (data.ptr == null)
|
||||
abort();
|
||||
|
||||
static immutable TlsArray init = TlsArray.init;
|
||||
memcpy(data.ptr, &init, data.length);
|
||||
(cast(TlsArray*) data).length = 32;
|
||||
return cast(TlsArray*) data.ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure array is large enough to hold an entry for offset.
|
||||
* Note: the array index will be offset - 1!
|
||||
*/
|
||||
void ensureLength(Value)(ref Array!(Value) arr, size_t offset) nothrow @nogc
|
||||
{
|
||||
// index is offset-1
|
||||
if (offset > arr.length)
|
||||
{
|
||||
auto newSize = arr.length * 2;
|
||||
if (offset > newSize)
|
||||
newSize = offset + 32;
|
||||
arr.length = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Public interface
|
||||
public:
|
||||
void _d_emutls_scan(scope void delegate(void* pbeg, void* pend) nothrow cb) nothrow
|
||||
{
|
||||
void scanArray(scope TlsArray* arr) nothrow
|
||||
{
|
||||
foreach (index, entry; *arr)
|
||||
{
|
||||
auto ptr = cast(void*) entry;
|
||||
if (ptr)
|
||||
cb(ptr, ptr + emutlsSizes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
__gthread_once(&initOnce, &_d_emutls_init);
|
||||
emutlsMutex.lock_nothrow();
|
||||
// this code is effectively nothrow
|
||||
try
|
||||
{
|
||||
foreach (arr, value; emutlsArrays)
|
||||
{
|
||||
scanArray(arr);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
emutlsMutex.unlock_nothrow();
|
||||
scanArray(&singleArray);
|
||||
}
|
||||
|
||||
// Call this after druntime has been unloaded
|
||||
void _d_emutls_destroy() nothrow @nogc
|
||||
{
|
||||
if (__gthread_key_delete(emutlsKey) != 0)
|
||||
abort();
|
||||
|
||||
(cast(Mutex) _emutlsMutex.ptr).__dtor();
|
||||
destroy(emutlsArrays);
|
||||
}
|
127
libphobos/libdruntime/gcc/gthread.d
Normal file
127
libphobos/libdruntime/gcc/gthread.d
Normal file
@ -0,0 +1,127 @@
|
||||
// GNU D Compiler thread support for emulated TLS routines.
|
||||
// Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
|
||||
// GCC is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation; either version 3, or (at your option) any later
|
||||
// version.
|
||||
|
||||
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
|
||||
// Under Section 7 of GPL version 3, you are granted additional
|
||||
// permissions described in the GCC Runtime Library Exception, version
|
||||
// 3.1, as published by the Free Software Foundation.
|
||||
|
||||
// You should have received a copy of the GNU General Public License and
|
||||
// a copy of the GCC Runtime Library Exception along with this program;
|
||||
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
module gcc.gthread;
|
||||
import gcc.config;
|
||||
|
||||
extern (C) nothrow @nogc:
|
||||
|
||||
alias GthreadDestroyFn = extern (C) void function(void*);
|
||||
alias GthreadOnceFn = extern (C) void function();
|
||||
|
||||
static if (GNU_Thread_Model == ThreadModel.Posix)
|
||||
{
|
||||
import core.sys.posix.pthread;
|
||||
|
||||
alias __gthread_key_create = pthread_key_create;
|
||||
alias __gthread_key_delete = pthread_key_delete;
|
||||
alias __gthread_getspecific = pthread_getspecific;
|
||||
alias __gthread_setspecific = pthread_setspecific;
|
||||
alias __gthread_once = pthread_once;
|
||||
alias __gthread_key_t = pthread_key_t;
|
||||
alias __gthread_once_t = pthread_once_t;
|
||||
enum GTHREAD_ONCE_INIT = PTHREAD_ONCE_INIT;
|
||||
|
||||
// TODO: FreeBSD and Solaris exposes a dummy POSIX threads
|
||||
// interface that will need to be handled here.
|
||||
extern (D) int __gthread_active_p()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else static if (GNU_Thread_Model == ThreadModel.Single)
|
||||
{
|
||||
alias __gthread_key_t = int;
|
||||
alias __gthread_once_t = int;
|
||||
enum GTHREAD_ONCE_INIT = 0;
|
||||
|
||||
extern (D) int __gthread_key_create(__gthread_key_t*, GthreadDestroyFn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern (D) int __gthread_key_delete(__gthread_key_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern (D) void* __gthread_getspecific(__gthread_key_t)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
extern (D) int __gthread_setspecific(__gthread_key_t, void*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern (D) int __gthread_once(__gthread_once_t*, GthreadOnceFn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern (D) int __gthread_active_p()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else static if (GNU_Thread_Model == ThreadModel.Win32)
|
||||
{
|
||||
struct __gthread_once_t
|
||||
{
|
||||
INT done;
|
||||
LONG started;
|
||||
}
|
||||
|
||||
int __gthr_win32_key_create(__gthread_key_t* keyp, GthreadDestroyFn dtor);
|
||||
int __gthr_win32_key_delete(__gthread_key_t key);
|
||||
void* __gthr_win32_getspecific(__gthread_key_t key);
|
||||
int __gthr_win32_setspecific(__gthread_key_t key, const void* ptr);
|
||||
int __gthr_win32_once(__gthread_once_t* once, GthreadOnceFn);
|
||||
|
||||
alias __gthread_key_create = __gthr_win32_key_create;
|
||||
alias __gthread_key_delete = __gthr_win32_key_delete;
|
||||
alias __gthread_getspecific = __gthr_win32_getspecific;
|
||||
alias __gthread_setspecific = __gthr_win32_setspecific;
|
||||
alias __gthread_once = __gthr_win32_once;
|
||||
enum GTHREAD_ONCE_INIT = __gthread_once_t(0, -1);
|
||||
alias __gthread_key_t = c_ulong;
|
||||
|
||||
version (MinGW)
|
||||
{
|
||||
// Mingw runtime >= v0.3 provides a magic variable that is set to nonzero
|
||||
// if -mthreads option was specified, or 0 otherwise.
|
||||
extern __gshared int _CRT_MT;
|
||||
}
|
||||
|
||||
extern (D) int __gthread_active_p()
|
||||
{
|
||||
version (MinGW)
|
||||
return _CRT_MT;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "Not implemented");
|
||||
}
|
@ -222,8 +222,16 @@ version (Shared)
|
||||
|
||||
void scanTLSRanges(Array!(ThreadDSO)* tdsos, scope ScanDG dg) nothrow
|
||||
{
|
||||
foreach (ref tdso; *tdsos)
|
||||
dg(tdso._tlsRange.ptr, tdso._tlsRange.ptr + tdso._tlsRange.length);
|
||||
version (GNU_EMUTLS)
|
||||
{
|
||||
import gcc.emutls;
|
||||
_d_emutls_scan(dg);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ref tdso; *tdsos)
|
||||
dg(tdso._tlsRange.ptr, tdso._tlsRange.ptr + tdso._tlsRange.length);
|
||||
}
|
||||
}
|
||||
|
||||
// interface for core.thread to inherit loaded libraries
|
||||
@ -310,8 +318,16 @@ else
|
||||
|
||||
void scanTLSRanges(Array!(void[])* rngs, scope ScanDG dg) nothrow
|
||||
{
|
||||
foreach (rng; *rngs)
|
||||
dg(rng.ptr, rng.ptr + rng.length);
|
||||
version (GNU_EMUTLS)
|
||||
{
|
||||
import gcc.emutls;
|
||||
_d_emutls_scan(dg);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (rng; *rngs)
|
||||
dg(rng.ptr, rng.ptr + rng.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -519,6 +535,11 @@ extern(C) void _d_dso_registry(CompilerDSOData* data)
|
||||
_handleToDSO.reset();
|
||||
}
|
||||
finiLocks();
|
||||
version (GNU_EMUTLS)
|
||||
{
|
||||
import gcc.emutls;
|
||||
_d_emutls_destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -805,40 +826,46 @@ void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
|
||||
break;
|
||||
|
||||
case PT_TLS: // TLS segment
|
||||
safeAssert(!pdso._tlsSize, "Multiple TLS segments in image header.");
|
||||
static if (OS_Have_Dlpi_Tls_Modid)
|
||||
version (GNU_EMUTLS)
|
||||
{
|
||||
pdso._tlsMod = info.dlpi_tls_modid;
|
||||
pdso._tlsSize = phdr.p_memsz;
|
||||
}
|
||||
else version (Solaris)
|
||||
{
|
||||
struct Rt_map
|
||||
{
|
||||
Link_map rt_public;
|
||||
const char* rt_pathname;
|
||||
c_ulong rt_padstart;
|
||||
c_ulong rt_padimlen;
|
||||
c_ulong rt_msize;
|
||||
uint rt_flags;
|
||||
uint rt_flags1;
|
||||
c_ulong rt_tlsmodid;
|
||||
}
|
||||
|
||||
Rt_map* map;
|
||||
version (Shared)
|
||||
dlinfo(handleForName(info.dlpi_name), RTLD_DI_LINKMAP, &map);
|
||||
else
|
||||
dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
|
||||
// Until Solaris 11.4, tlsmodid for the executable is 0.
|
||||
// Let it start at 1 as the rest of the code expects.
|
||||
pdso._tlsMod = map.rt_tlsmodid + 1;
|
||||
pdso._tlsSize = phdr.p_memsz;
|
||||
}
|
||||
else
|
||||
{
|
||||
pdso._tlsMod = 0;
|
||||
pdso._tlsSize = 0;
|
||||
safeAssert(!pdso._tlsSize, "Multiple TLS segments in image header.");
|
||||
static if (OS_Have_Dlpi_Tls_Modid)
|
||||
{
|
||||
pdso._tlsMod = info.dlpi_tls_modid;
|
||||
pdso._tlsSize = phdr.p_memsz;
|
||||
}
|
||||
else version (Solaris)
|
||||
{
|
||||
struct Rt_map
|
||||
{
|
||||
Link_map rt_public;
|
||||
const char* rt_pathname;
|
||||
c_ulong rt_padstart;
|
||||
c_ulong rt_padimlen;
|
||||
c_ulong rt_msize;
|
||||
uint rt_flags;
|
||||
uint rt_flags1;
|
||||
c_ulong rt_tlsmodid;
|
||||
}
|
||||
|
||||
Rt_map* map;
|
||||
version (Shared)
|
||||
dlinfo(handleForName(info.dlpi_name), RTLD_DI_LINKMAP, &map);
|
||||
else
|
||||
dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
|
||||
// Until Solaris 11.4, tlsmodid for the executable is 0.
|
||||
// Let it start at 1 as the rest of the code expects.
|
||||
pdso._tlsMod = map.rt_tlsmodid + 1;
|
||||
pdso._tlsSize = phdr.p_memsz;
|
||||
}
|
||||
else
|
||||
{
|
||||
pdso._tlsMod = 0;
|
||||
pdso._tlsSize = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -0,0 +1,50 @@
|
||||
import core.memory, core.thread, core.bitop;
|
||||
|
||||
/*
|
||||
* This test repeatedly performs operations on GC-allocated objects which
|
||||
* are only reachable from TLS storage. Tests are performed in multiple threads
|
||||
* and GC collections are triggered repeatedly, so if the GC does not properly
|
||||
* scan TLS memory, this provokes a crash.
|
||||
*/
|
||||
class TestTLS
|
||||
{
|
||||
uint a;
|
||||
void addNumber()
|
||||
{
|
||||
auto val = volatileLoad(&a);
|
||||
val++;
|
||||
volatileStore(&a, val);
|
||||
}
|
||||
}
|
||||
|
||||
TestTLS tlsPtr;
|
||||
|
||||
static this()
|
||||
{
|
||||
tlsPtr = new TestTLS();
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
void runThread()
|
||||
{
|
||||
for (size_t i = 0; i < 100; i++)
|
||||
{
|
||||
Thread.sleep(10.msecs);
|
||||
tlsPtr.addNumber();
|
||||
GC.collect();
|
||||
}
|
||||
}
|
||||
|
||||
Thread[] threads;
|
||||
for (size_t i = 0; i < 20; i++)
|
||||
{
|
||||
auto t = new Thread(&runThread);
|
||||
threads ~= t;
|
||||
t.start();
|
||||
}
|
||||
runThread();
|
||||
|
||||
foreach (thread; threads)
|
||||
thread.join();
|
||||
}
|
Loading…
Reference in New Issue
Block a user