Skip to content

Commit fcbe2e6

Browse files
authored
sync: add thread local storage (TLS) support (#24849)
1 parent 952f63e commit fcbe2e6

5 files changed

Lines changed: 521 additions & 0 deletions

File tree

‎vlib/sync/thread_windows.c.v‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
module sync
22

3+
// Get current thread ID from Windows API (via C interface)
34
fn C.GetCurrentThreadId() u32
45

6+
// thread_id returns a unique identifier for the caller thread.
57
pub fn thread_id() u64 {
68
return u64(C.GetCurrentThreadId())
79
}

‎vlib/sync/tls.v‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
module sync
2+
3+
// ThreadLocalStorage provides thread-local storage for values of type T
4+
@[noinit]
5+
struct ThreadLocalStorage[T] {
6+
mut:
7+
key u64 // TLS key identifier. Note: While Linux uses unsigned int, Darwin requires unsigned long. u64 accommodates both.
8+
in_use bool // Allocation status flag
9+
}
10+
11+
// DataConversion convert voidptr to/from type T
12+
union DataConversion {
13+
mut:
14+
f_u8 u8
15+
f_u16 u16
16+
f_u32 u32
17+
f_u64 u64
18+
19+
f_i8 i8
20+
f_i16 i16
21+
f_i32 i32
22+
f_i64 i64
23+
24+
f_voidptr voidptr
25+
f_isize isize
26+
f_usize usize
27+
f_int int
28+
f_f32 f32
29+
f_f64 f64
30+
f_rune rune
31+
}
32+
33+
// convert_t_to_voidptr convert value from type T to voidptr
34+
@[inline]
35+
fn convert_t_to_voidptr[T](value T) !voidptr {
36+
mut f := DataConversion{}
37+
$if T is i8 {
38+
f.f_i8 = value
39+
} $else $if T is i16 {
40+
f.f_i16 = value
41+
} $else $if T is i32 {
42+
f.f_i32 = value
43+
} $else $if T is i64 {
44+
f.f_i64 = value
45+
} $else $if T is u8 {
46+
f.f_u8 = value
47+
} $else $if T is u16 {
48+
f.f_u16 = value
49+
} $else $if T is u32 {
50+
f.f_u32 = value
51+
} $else $if T is u64 {
52+
f.f_u64 = value
53+
} $else $if T is $pointer {
54+
f.f_voidptr = voidptr(value)
55+
} $else $if T is isize {
56+
f.f_isize = value
57+
} $else $if T is usize {
58+
f.f_usize = value
59+
} $else $if T is int {
60+
f.f_int = value
61+
} $else $if T is f32 {
62+
f.f_f32 = value
63+
} $else $if T is f64 {
64+
f.f_f64 = value
65+
} $else $if T is rune {
66+
f.f_rune = value
67+
} $else {
68+
return error('Unsupported data type `${T.name}` to voidptr')
69+
}
70+
return unsafe { f.f_voidptr }
71+
}
72+
73+
// convert_voidptr_to_t convert value from voidptr to type T
74+
@[inline]
75+
fn convert_voidptr_to_t[T](value voidptr) !T {
76+
f := DataConversion{
77+
f_voidptr: value
78+
}
79+
$if T is i8 {
80+
return unsafe { f.f_i8 }
81+
} $else $if T is i16 {
82+
return unsafe { f.f_i16 }
83+
} $else $if T is i32 {
84+
return unsafe { f.f_i32 }
85+
} $else $if T is i64 {
86+
return unsafe { f.f_i64 }
87+
} $else $if T is u8 {
88+
return unsafe { f.f_u8 }
89+
} $else $if T is u16 {
90+
return unsafe { f.f_u16 }
91+
} $else $if T is u32 {
92+
return unsafe { f.f_u32 }
93+
} $else $if T is u64 {
94+
return unsafe { f.f_u64 }
95+
} $else $if T is $pointer {
96+
return unsafe { f.f_voidptr }
97+
} $else $if T is isize {
98+
return unsafe { f.f_isize }
99+
} $else $if T is usize {
100+
return unsafe { f.f_usize }
101+
} $else $if T is int {
102+
return unsafe { f.f_int }
103+
} $else $if T is f32 {
104+
return unsafe { f.f_f32 }
105+
} $else $if T is f64 {
106+
return unsafe { f.f_f64 }
107+
} $else $if T is rune {
108+
return unsafe { f.f_rune }
109+
}
110+
return error('Unsupported data type `${T.name}` from voidptr')
111+
}

‎vlib/sync/tls_default.c.v‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
module sync
2+
3+
// Pthread TLS API functions (via C interface)
4+
fn C.pthread_key_create(key voidptr, voidptr) int
5+
fn C.pthread_key_delete(key u64) int
6+
fn C.pthread_setspecific(key u64, const_ptr voidptr) int
7+
fn C.pthread_getspecific(key u64) voidptr
8+
9+
// new_tls creates new TLS storage initialized with the given `value`
10+
pub fn new_tls[T](value T) !ThreadLocalStorage[T] {
11+
$if T !in [i8, i16, i32, i64, u8, u16, u32, u64, isize, usize, f32, f64, rune, int, voidptr,
12+
$pointer] {
13+
return error('new_tls: invalid type ${T.name}')
14+
}
15+
mut key := u64(0)
16+
// Validate key allocation
17+
if C.pthread_key_create(voidptr(&key), 0) == 0 {
18+
// Initialize storage and verify success
19+
if C.pthread_setspecific(key, convert_t_to_voidptr(value)!) == 0 {
20+
return ThreadLocalStorage[T]{
21+
key: key
22+
in_use: true
23+
}
24+
} else {
25+
return error('new_tls: Failed to initialize TLS value: ${value}')
26+
}
27+
}
28+
29+
// Handle allocation failure
30+
return error('new_tls: Failed to allocate TLS index')
31+
}
32+
33+
// set updates the `value` in TLS storage.
34+
@[inline]
35+
pub fn (mut t ThreadLocalStorage[T]) set(value T) ! {
36+
if t.in_use {
37+
if C.pthread_setspecific(t.key, convert_t_to_voidptr(value)!) != 0 {
38+
return error('set: Failed to update TLS value: ${value}')
39+
}
40+
} else {
41+
return error('set: TLS storage is already destroyed')
42+
}
43+
}
44+
45+
// get retrieves the current value from TLS storage.
46+
@[inline]
47+
pub fn (mut t ThreadLocalStorage[T]) get() !T {
48+
if t.in_use {
49+
return convert_voidptr_to_t[T](C.pthread_getspecific(t.key))
50+
}
51+
return error('get: TLS storage is already destroyed')
52+
}
53+
54+
// destroy releases TLS resources (must be called manually).
55+
@[inline]
56+
pub fn (mut t ThreadLocalStorage[T]) destroy() ! {
57+
if C.pthread_key_delete(t.key) == 0 {
58+
t.in_use = false
59+
} else {
60+
return error('destroy: Failed to release TLS resources')
61+
}
62+
}

0 commit comments

Comments
 (0)