Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PCRE2: optimize memory allocations #15395

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
52 changes: 52 additions & 0 deletions src/crystal/system/thread_local.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Stores and retrieves a `Pointer` or `Reference` in the system Thread Local
# Storage (TLS), also named Thread Specific Storage (TSS).
#
# WARNING: Thread local storage is unknown to the GC. When storing a `Reference`
# or a `Pointer` to the GC HEAP memory, you must make sure that there are other
# live references to that memory, otherwise the GC might decide to collect it!
struct Crystal::System::ThreadLocal(T)
# Initializes a global thread local index/key.
def initialize
raise NotImplementedError.new
end

# Initializes a global thread local index/key. The destructor must be called
# in each thread that terminates if the thread local value has been set.
def initialize(&destructor : T ->)
raise NotImplementedError.new
end

# Returns the thread local value if previously set, otherwise runs the
# initializer to create the value, sets the thread local and returns it.
def get(&initializer : -> T) : T
get? || set(yield)
end

# Returns the thread local value if previously set, otherwise returns `nil`.
def get? : T?
raise NotImplementedError.new
end

# Sets the thread local to *value*. Doesn't call the destructor when replacing
# a thread local value; you are responsible for retrieving the previous value
# and doing any cleanup. Returns *value*.
def set(value : T) : T
raise NotImplementedError.new
end

# Releases the thread local index/key. Assumes every thread will no longer
# need to access this thread local.
def release : Nil
raise NotImplementedError.new
end
end

{% if flag?(:wasm32) %}
require "./wasi/thread_local"
{% elsif flag?(:unix) %}
require "./unix/thread_local"
{% elsif flag?(:win32) %}
require "./win32/thread_local"
{% else %}
{% raise "unsupported target" %}
{% end %}
35 changes: 14 additions & 21 deletions src/crystal/system/unix/pthread.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require "c/pthread"
require "c/sched"
require "../panic"
{% if flag?(:openbsd) || flag?(:android) %}
require "../thread_local"
{% end %}

module Crystal::System::Thread
alias Handle = LibC::PthreadT
Expand Down Expand Up @@ -30,9 +33,7 @@ module Crystal::System::Thread
{% if flag?(:musl) %}
@@main_handle = current_handle
{% elsif flag?(:openbsd) || flag?(:android) %}
ret = LibC.pthread_key_create(out current_key, nil)
raise RuntimeError.from_os_error("pthread_key_create", Errno.new(ret)) unless ret == 0
@@current_key = current_key
@@current_thread = ThreadLocal(::Thread).new
{% end %}
end

Expand All @@ -57,37 +58,29 @@ module Crystal::System::Thread
raise RuntimeError.from_errno("sched_yield") unless ret == 0
end

# no thread local storage (TLS) for OpenBSD,
# we use pthread's specific storage (TSS) instead
# No support for @[::ThreadLocal] for OpenBSD, we use pthread's specific storage (TSS)
# instead
#
# Android appears to support TLS to some degree, but executables fail with
# Android appears to support @[::ThreadLocal] to some degree, but executables fail with
# an underaligned TLS segment, see https://github.com/crystal-lang/crystal/issues/13951
{% if flag?(:openbsd) || flag?(:android) %}
@@current_key = uninitialized LibC::PthreadKeyT
@@current_thread = uninitialized ThreadLocal(::Thread)

def self.current_thread : ::Thread
if ptr = LibC.pthread_getspecific(@@current_key)
ptr.as(::Thread)
else
# Thread#start sets `Thread.current` as soon it starts. Thus we know
# that if `Thread.current` is not set then we are in the main thread
self.current_thread = ::Thread.new
end
# Thread#start sets Thread.current as soon as it starts. Thus we know
# that if `Thread.current` is not set then we are in the main thread
@@current_thread.get { ::Thread.new }
end

def self.current_thread? : ::Thread?
if ptr = LibC.pthread_getspecific(@@current_key)
ptr.as(::Thread)
end
@@current_thread.get?
end

def self.current_thread=(thread : ::Thread)
ret = LibC.pthread_setspecific(@@current_key, thread.as(Void*))
raise RuntimeError.from_os_error("pthread_setspecific", Errno.new(ret)) unless ret == 0
thread
@@current_thread.set(thread)
end
{% else %}
@[ThreadLocal]
@[::ThreadLocal]
@@current_thread : ::Thread?

def self.current_thread : ::Thread
Expand Down
33 changes: 33 additions & 0 deletions src/crystal/system/unix/thread_local.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "c/pthread"

struct Crystal::System::ThreadLocal(T)
@key : LibC::PthreadKeyT

def initialize
{% raise "Can only create Crystal::System::ThreadLocal with pointer types or reference types, not #{T}" unless T < Pointer || T < Reference %}
ret = LibC.pthread_key_create(out @key, nil)
raise RuntimeError.from_os_error("pthread_key_create", Errno.new(ret)) unless ret == 0
end

def initialize(&destructor : T ->)
{% raise "Can only create Crystal::System::ThreadLocal with pointer types or reference types, not #{T}" unless T < Pointer || T < Reference %}
ret = LibC.pthread_key_create(out @key, destructor.unsafe_as(Proc(Void*, Nil)))
raise RuntimeError.from_os_error("pthread_key_create", Errno.new(ret)) unless ret == 0
end

def get? : T?
ptr = LibC.pthread_getspecific(@key)
ptr.as(T) if ptr
end

def set(value : T) : T
ret = LibC.pthread_setspecific(@key, value.as(Void*))
raise RuntimeError.from_os_error("pthread_setspecific", Errno.new(ret)) unless ret == 0
value
end

def release : Nil
ret = LibC.pthread_key_delete(@key)
raise RuntimeError.from_os_error("pthread_key_delete", Errno.new(ret)) unless ret == 0
end
end
26 changes: 26 additions & 0 deletions src/crystal/system/wasi/thread_local.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# TODO: we don't support threads in WASI yet; the thread local is just a faking
# it for the time being.

struct Crystal::System::ThreadLocal(T)
@value : T?

def initialize
{% raise "Can only create Crystal::System::ThreadLocal with pointer types or reference types, not #{T}" unless T < Pointer || T < Reference %}
end

def initialize(&destructor : T ->)
{% raise "Can only create Crystal::System::ThreadLocal with pointer types or reference types, not #{T}" unless T < Pointer || T < Reference %}
end

def get? : T?
@value
end

def set(value : T) : T
@value = value
end

def release : Nil
@value = nil
end
end
35 changes: 11 additions & 24 deletions src/crystal/system/win32/thread.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require "c/processthreadsapi"
require "c/synchapi"
require "../panic"
{% if flag?(:gnu) %}
require "../thread_local"
{% end %}

module Crystal::System::Thread
alias Handle = LibC::HANDLE
Expand All @@ -22,11 +25,7 @@ module Crystal::System::Thread

def self.init : Nil
{% if flag?(:gnu) %}
current_key = LibC.TlsAlloc
if current_key == LibC::TLS_OUT_OF_INDEXES
Crystal::System.panic("TlsAlloc()", WinError.value)
end
@@current_key = current_key
@@current_thread = ThreadLocal(::Thread).new
{% end %}
end

Expand Down Expand Up @@ -55,37 +54,25 @@ module Crystal::System::Thread
LibC.SwitchToThread
end

# MinGW does not support TLS correctly
# MinGW does not support @[::ThreadLocal] correctly
{% if flag?(:gnu) %}
@@current_key = uninitialized LibC::DWORD
@@current_thread = uninitialized ThreadLocal(::Thread)

def self.current_thread : ::Thread
th = current_thread?
return th if th

# Thread#start sets `Thread.current` as soon it starts. Thus we know
# Thread#start sets Thread.current as soon as it starts. Thus we know
# that if `Thread.current` is not set then we are in the main thread
self.current_thread = ::Thread.new
@@current_thread.get { ::Thread.new }
end

def self.current_thread? : ::Thread?
ptr = LibC.TlsGetValue(@@current_key)
err = WinError.value
unless err == WinError::ERROR_SUCCESS
Crystal::System.panic("TlsGetValue()", err)
end

ptr.as(::Thread?)
@@current_thread.get?
end

def self.current_thread=(thread : ::Thread)
if LibC.TlsSetValue(@@current_key, thread.as(Void*)) == 0
Crystal::System.panic("TlsSetValue()", WinError.value)
end
thread
@@current_thread.set(thread)
end
{% else %}
@[ThreadLocal]
@[::ThreadLocal]
@@current_thread : ::Thread?

def self.current_thread : ::Thread
Expand Down
38 changes: 38 additions & 0 deletions src/crystal/system/win32/thread_local.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# NOTE: Uses FLS instead of TLS so we can register a destructor while creating
# the key. Since we don't use the Windows Fiber API, FLS acts exactly the same
# as TLS.
#
# See <https://learn.microsoft.com/en-us/windows/win32/procthread/fibers#fiber-local-storage>
require "c/fibersapi"

struct Crystal::System::ThreadLocal(T)
@key : LibC::DWORD

def initialize
{% raise "Can only create Crystal::System::ThreadLocal with pointer types or reference types, not #{T}" unless T < Pointer || T < Reference %}
@key = LibC.FlsAlloc(nil)
raise RuntimeError.from_winerror("FlsAlloc") if @key == LibC::FLS_OUT_OF_INDEXES
end

def initialize(&destructor : T ->)
{% raise "Can only create Crystal::System::ThreadLocal with pointer types or reference types, not #{T}" unless T < Pointer || T < Reference %}
@key = LibC.FlsAlloc(destructor.unsafe_as(Proc(Void*, Nil)))
raise RuntimeError.from_winerror("FlsAlloc") if @key == LibC::FLS_OUT_OF_INDEXES
end

def get? : T?
ptr = LibC.FlsGetValue(@key)
ptr.as(T) if ptr
end

def set(value : T) : T
ret = LibC.FlsSetValue(@key, value.as(Void*))
raise RuntimeError.from_winerror("FlsSetValue") unless ret
value
end

def release : Nil
ret = LibC.FlsFree(@key)
raise RuntimeError.from_winerror("FlsFree") unless ret
end
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-android/c/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ lib LibC
fun pthread_getspecific(__key : PthreadKeyT) : Void*
fun pthread_join(__pthread : PthreadT, __return_value_ptr : Void**) : Int
fun pthread_key_create(__key_ptr : PthreadKeyT*, __key_destructor : Void* ->) : Int
fun pthread_key_delete(__key_ptr : PthreadKeyT) : Int

fun pthread_mutexattr_destroy(__attr : PthreadMutexattrT*) : Int
fun pthread_mutexattr_init(__attr : PthreadMutexattrT*) : Int
Expand Down
5 changes: 5 additions & 0 deletions src/lib_c/aarch64-darwin/c/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ lib LibC
fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int
fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int
fun pthread_detach(x0 : PthreadT) : Int
fun pthread_getspecific(PthreadKeyT) : Void*
fun pthread_get_stackaddr_np(x0 : PthreadT) : Void*
fun pthread_get_stacksize_np(x0 : PthreadT) : SizeT
fun pthread_join(x0 : PthreadT, x1 : Void**) : Int
alias PthreadKeyDestructor = (Void*) ->
fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int
fun pthread_key_delete(PthreadKeyT) : Int
fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int
fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int
fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int
Expand All @@ -26,4 +30,5 @@ lib LibC
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
fun pthread_self : PthreadT
fun pthread_setname_np(Char*) : Int
fun pthread_setspecific(PthreadKeyT, Void*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-darwin/c/sys/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ lib LibC
end

type PthreadT = Void*
alias PthreadKeyT = UInt
alias SSizeT = Long
alias SusecondsT = Int
alias TimeT = Long
Expand Down
5 changes: 5 additions & 0 deletions src/lib_c/aarch64-linux-gnu/c/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ lib LibC
fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int
fun pthread_detach(th : PthreadT) : Int
fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int
fun pthread_getspecific(PthreadKeyT) : Void*
fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int
fun pthread_join(th : PthreadT, thread_return : Void**) : Int
alias PthreadKeyDestructor = (Void*) ->
fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int
fun pthread_key_delete(PthreadKeyT) : Int
fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int
fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int
fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int
Expand All @@ -37,4 +41,5 @@ lib LibC
fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int
fun pthread_self : PthreadT
fun pthread_setname_np(PthreadT, Char*) : Int
fun pthread_setspecific(PthreadKeyT, Void*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-linux-gnu/c/sys/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ lib LibC
end

alias PthreadT = ULong
alias PthreadKeyT = UInt
alias SSizeT = Long
alias SusecondsT = Long
alias TimeT = Long
Expand Down
5 changes: 5 additions & 0 deletions src/lib_c/aarch64-linux-musl/c/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ lib LibC
fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int
fun pthread_detach(x0 : PthreadT) : Int
fun pthread_getattr_np(x0 : PthreadT, x1 : PthreadAttrT*) : Int
fun pthread_getspecific(PthreadKeyT) : Void*
fun pthread_join(x0 : PthreadT, x1 : Void**) : Int
alias PthreadKeyDestructor = (Void*) ->
fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int
fun pthread_key_delete(PthreadKeyT) : Int
fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int
fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int
fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int
Expand All @@ -28,4 +32,5 @@ lib LibC
fun pthread_mutex_unlock(x0 : PthreadMutexT*) : Int
fun pthread_self : PthreadT
fun pthread_setname_np(PthreadT, Char*) : Int
fun pthread_setspecific(PthreadKeyT, Void*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-linux-musl/c/sys/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ lib LibC
end

type PthreadT = Void*
alias PthreadKeyT = UInt
alias SSizeT = Long
alias SusecondsT = Long
alias TimeT = Long
Expand Down
5 changes: 5 additions & 0 deletions src/lib_c/arm-linux-gnueabihf/c/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ lib LibC
fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int
fun pthread_detach(th : PthreadT) : Int
fun pthread_getattr_np(thread : PthreadT, attr : PthreadAttrT*) : Int
fun pthread_getspecific(PthreadKeyT) : Void*
fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int
fun pthread_join(th : PthreadT, thread_return : Void**) : Int
alias PthreadKeyDestructor = (Void*) ->
fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int
fun pthread_key_delete(PthreadKeyT) : Int
fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int
fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int
fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int
Expand All @@ -37,4 +41,5 @@ lib LibC
fun pthread_mutex_unlock(mutex : PthreadMutexT*) : Int
fun pthread_self : PthreadT
fun pthread_setname_np(PthreadT, Char*) : Int
fun pthread_setspecific(PthreadKeyT, Void*) : Int
end
Loading
Loading