Skip to content

Commit ffca40e

Browse files
author
adeyblue
committed
Implemented ThreadSelf and the surprising amount of plumbing it required. Plus tests and bi
1 parent 8612383 commit ffca40e

15 files changed

+448
-32
lines changed

inc/fbthread.bi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#pragma once
22

33
declare sub ThreadDetach alias "fb_ThreadDetach"( byval thread as any ptr )
4+
declare function ThreadSelf alias "fb_ThreadSelf"( ) as any ptr

src/rtlib/dos/symb_reg.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ extern_asm(_fb_LPrintLongint);
405405
extern_asm(_fb_ConsoleViewEx);
406406
extern_asm(_fb_hSetFileBufSize);
407407
extern_asm(_fb_ThreadCall);
408+
extern_asm(_fb_ThreadSelf);
408409
extern_asm(___fb_utf8_offsetsTb);
409410
extern_asm(_fb_StrFill2);
410411
extern_asm(_fb_WstrOct_l);
@@ -1245,6 +1246,7 @@ DXE_EXPORT_TABLE (libfb_symbol_table)
12451246
DXE_EXPORT_ASM (_fb_ConsoleViewEx)
12461247
DXE_EXPORT_ASM (_fb_hSetFileBufSize)
12471248
DXE_EXPORT_ASM (_fb_ThreadCall)
1249+
DXE_EXPORT_ASM (_fb_ThreadSelf)
12481250
DXE_EXPORT_ASM (___fb_utf8_offsetsTb)
12491251
DXE_EXPORT_ASM (_fb_StrFill2)
12501252
DXE_EXPORT_ASM (_fb_WstrOct_l)

src/rtlib/dos/thread_core.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
static void *threadproc( void *param )
99
{
1010
FBTHREADINFO *info = param;
11+
FBTHREAD *thread = info->thread;
12+
13+
FB_TLSGETCTX( FBTHREAD )->self = thread;
1114

1215
/* call the user thread */
1316
info->proc( info->param );
14-
free( info);
17+
free( info );
1518

1619
/* free mem */
1720
fb_TlsFreeCtxTb( );
@@ -42,6 +45,8 @@ FBCALL FBTHREAD *fb_ThreadCreate( FB_THREADPROC proc, void *param, ssize_t stack
4245

4346
info->proc = proc;
4447
info->param = param;
48+
info->thread = thread;
49+
thread->flags = 0;
4550

4651
if( pthread_attr_init( &tattr ) ) {
4752
free( thread );
@@ -63,6 +68,7 @@ FBCALL FBTHREAD *fb_ThreadCreate( FB_THREADPROC proc, void *param, ssize_t stack
6368
}
6469

6570
pthread_attr_destroy( &tattr );
71+
6672
return thread;
6773

6874
#else
@@ -73,14 +79,18 @@ FBCALL FBTHREAD *fb_ThreadCreate( FB_THREADPROC proc, void *param, ssize_t stack
7379

7480
FBCALL void fb_ThreadWait( FBTHREAD *thread )
7581
{
76-
7782
#if defined ENABLE_MT
78-
if( thread == NULL )
83+
/* A wait for the main thread or ourselves will never end */
84+
if(
85+
( thread == NULL ) ||
86+
( ( thread->flags & FBTHREAD_MAIN ) != 0 ) ||
87+
( thread == FB_TLSGETCTX( FBTHREAD )->self )
88+
) {
7989
return;
90+
}
8091

8192
pthread_join( thread->id, NULL );
8293

8394
free( thread );
8495
#endif
85-
8696
}

src/rtlib/fb_private_thread.h

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@
1515
};
1616
#endif
1717

18-
/* Info structure passed to our internal threadproc()s, so it can call the
19-
user's threadproc (freed at the end of our threadproc()s) */
20-
typedef struct {
21-
FB_THREADPROC proc;
22-
void *param;
23-
} FBTHREADINFO;
24-
2518
/* Thread handle returned by threadcreate(), so the caller is able to track the
2619
thread (freed by threadwait/threaddetach).
2720
@@ -33,6 +26,14 @@ typedef struct {
3326
With pthreads though, it's not clear whether we could store a pthread_t into
3427
a void*, because pthread_t doesn't have to be an integer or pointer, and
3528
furthermore, zero may be a valid value for it. */
29+
30+
typedef enum _FBTHREADFLAGS
31+
{
32+
FBTHREAD_MAIN = 1,
33+
FBTHREAD_EXITED = 2,
34+
FBTHREAD_DETACHED = 4
35+
} FBTHREADFLAGS;
36+
3637
struct _FBTHREAD {
3738
#if defined HOST_DOS && defined ENABLE_MT
3839
pthread_t id;
@@ -48,4 +49,28 @@ struct _FBTHREAD {
4849
#else
4950
#error Unexpected target
5051
#endif
52+
volatile FBTHREADFLAGS flags;
5153
};
54+
55+
/* Info structure passed to our internal threadproc()s, so it can call the
56+
user's threadproc (freed at the end of our threadproc()s) */
57+
typedef struct {
58+
FB_THREADPROC proc;
59+
void *param;
60+
FBTHREAD *thread;
61+
} FBTHREADINFO;
62+
63+
typedef struct _FB_FBTHREADCTX
64+
{
65+
FBTHREAD *self;
66+
} FB_FBTHREADCTX;
67+
68+
#define fb_FBTHREADCTX_Destructor NULL
69+
70+
void fb_AllocateMainFBThread( void );
71+
FBTHREADFLAGS fb_AtomicSetThreadFlags( volatile FBTHREADFLAGS *flags, FBTHREADFLAGS newFlag );
72+
73+
#ifdef ENABLE_MT
74+
void fb_CloseAtomicFBThreadFlagMutex( void );
75+
#endif
76+

src/rtlib/fb_thread.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ struct _FBCOND;
1010
typedef struct _FBCOND FBCOND;
1111

1212
FBCALL FBTHREAD *fb_ThreadCreate( FB_THREADPROC proc, void *param, ssize_t stack_size );
13+
FBCALL FBTHREAD *fb_ThreadSelf ( void );
1314
FBCALL void fb_ThreadWait ( FBTHREAD *thread );
1415
FBCALL void fb_ThreadDetach( FBTHREAD *thread );
1516

@@ -36,6 +37,7 @@ enum {
3637
FB_TLSKEY_INPUT,
3738
FB_TLSKEY_PRINTUSG,
3839
FB_TLSKEY_GFX,
40+
FB_TLSKEY_FBTHREAD,
3941
FB_TLSKEYS
4042
};
4143

@@ -47,12 +49,12 @@ typedef struct _FB_TLS_CTX_HEADER {
4749

4850
} FB_TLS_CTX_HEADER;
4951

50-
FBCALL void *fb_TlsGetCtx ( int index, size_t len, FB_TLS_DESTRUCTOR destructor );
51-
FBCALL void fb_TlsDelCtx ( int index );
52-
FBCALL void fb_TlsFreeCtxTb( void );
52+
FBCALL void *fb_TlsGetCtx ( int index, size_t len, FB_TLS_DESTRUCTOR destructor );
53+
FBCALL void fb_TlsDelCtx ( int index );
54+
FBCALL void fb_TlsFreeCtxTb ( void );
5355
#ifdef ENABLE_MT
54-
void fb_TlsInit ( void );
55-
void fb_TlsExit ( void );
56+
void fb_TlsInit ( void );
57+
void fb_TlsExit ( void );
5658
#endif
5759

5860
#define FB_TLSGETCTX(id) ((FB_##id##CTX *)fb_TlsGetCtx( FB_TLSKEY_##id, sizeof( FB_##id##CTX ), fb_##id##CTX_Destructor ))

src/rtlib/init.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "fb.h"
44
#include <locale.h>
5+
#include "fb_private_thread.h"
56

67
FB_RTLIB_CTX __fb_ctx /* not initialized */;
78
static int __fb_is_inicnt = 0;
@@ -23,6 +24,7 @@ void fb_hRtInit( void )
2324
#ifdef ENABLE_MT
2425
fb_TlsInit( );
2526
#endif
27+
fb_AllocateMainFBThread();
2628

2729
/**
2830
* With the default "C" locale (which is just plain 7-bit ASCII),

src/rtlib/thread_ctx.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
#include "fb.h"
44
#include "fb_private_thread.h"
5-
#include "fb_gfx_private.h"
6-
#include <assert.h>
75

86
#if defined ENABLE_MT && defined HOST_UNIX
97
#define FB_TLSENTRY pthread_key_t
@@ -55,7 +53,7 @@ FBCALL void *fb_TlsGetCtx( int index, size_t len, FB_TLS_DESTRUCTOR destructorFn
5553
else {
5654
FB_TLS_CTX_HEADER *ctxHeader = FB_TLS_DATA_TO_HEADER( ctx );
5755
/* The && is intentional here so if the assert fires, the message is displayed too */
58-
assert( (ctxHeader->destructor == destructorFn) && "fb_TlsGetCtx trying to set different destructor for existing data" );
56+
DBG_ASSERT( (ctxHeader->destructor == destructorFn) && "fb_TlsGetCtx trying to set different destructor for existing data" );
5957
}
6058
#endif
6159

@@ -102,5 +100,6 @@ void fb_TlsExit( void )
102100
for( i = 0; i < FB_TLSKEYS; i++ ) {
103101
FB_TLSFREE( __fb_tls_ctxtb[i] );
104102
}
103+
fb_CloseAtomicFBThreadFlagMutex( );
105104
}
106105
#endif

src/rtlib/thread_self.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#include "fb.h"
2+
#include "fb_private_thread.h"
3+
4+
#define GCC_VERSION (__GNUC__ * 10000 \
5+
+ __GNUC_MINOR__ * 100 \
6+
+ __GNUC_PATCHLEVEL__)
7+
8+
/* A mutex is massively overkill for atomic int updates,
9+
but we have to support both ancient platforms and build tools
10+
so those can use the super slow mutex and everything not 20 years old
11+
can use the fast stuff
12+
13+
GCC 4.1 is the first to implement the atomic builtins,
14+
and InterlockedOr is a macro in the Windows headers for
15+
atomic or operations
16+
17+
NeedsMutex = (EnableMT && (GCC <= 4.0 || !(Windows && InterlockedOr)))
18+
*/
19+
#if ( ( defined( ENABLE_MT ) ) && \
20+
( ( GCC_VERSION < 40100 ) || \
21+
( !( defined( HOST_WIN32 ) && defined( InterlockedOr ) ) ) \
22+
) \
23+
)
24+
#define NEEDS_MUTEX 1
25+
static FBMUTEX *g_threadFlagMutex = NULL;
26+
#endif
27+
28+
29+
FBCALL FBTHREAD *fb_ThreadSelf( void )
30+
{
31+
return FB_TLSGETCTX( FBTHREAD )->self;
32+
}
33+
34+
void fb_AllocateMainFBThread( void )
35+
{
36+
/* As TlsGetCtx will allocate a TLS structure and
37+
space to hold the context, and as the main thread doesn't have
38+
its own FBTHREAD* - to avoid making two allocations,
39+
we tell it the context is big enough to cover both
40+
and make our own FBTHREAD
41+
*/
42+
FB_FBTHREADCTX* ctx = fb_TlsGetCtx(
43+
FB_TLSKEY_FBTHREAD,
44+
sizeof( *ctx ) + sizeof( *( ctx->self ) ),
45+
fb_FBTHREADCTX_Destructor
46+
);
47+
ctx->self = ( FBTHREAD * )( ctx + 1 );
48+
memset( ctx->self, 0, sizeof( *( ctx->self ) ) );
49+
ctx->self->flags = FBTHREAD_MAIN;
50+
51+
#ifdef NEEDS_MUTEX
52+
g_threadFlagMutex = fb_MutexCreate( );
53+
#endif
54+
}
55+
56+
#ifdef ENABLE_MT
57+
void fb_CloseAtomicFBThreadFlagMutex( void )
58+
{
59+
#ifdef NEEDS_MUTEX
60+
fb_MutexDestroy( g_threadFlagMutex );
61+
#endif
62+
}
63+
64+
FBTHREADFLAGS fb_AtomicSetThreadFlags( volatile FBTHREADFLAGS *threadFlags, FBTHREADFLAGS newFlag )
65+
{
66+
67+
#ifdef NEEDS_MUTEX
68+
69+
FBTHREADFLAGS before;
70+
fb_MutexLock( g_threadFlagMutex );
71+
before = *threadFlags;
72+
*threadFlags |= newFlag;
73+
fb_MutexUnlock( g_threadFlagMutex );
74+
return before;
75+
76+
#else
77+
78+
#if GCC_VERSION >= 40100
79+
return __sync_fetch_and_or( threadFlags, newFlag );
80+
#elif defined ( InterlockedOr )
81+
return InterlockedOr( (volatile LONG *) threadFlags, newFlag );
82+
#endif /* GCC_VERSION */
83+
84+
#endif /* NEEDS_MUTEX */
85+
86+
}
87+
88+
#endif /* ENABLE_MT */

src/rtlib/unix/thread_core.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,23 @@
77
static void *threadproc( void *param )
88
{
99
FBTHREADINFO *info = param;
10+
FBTHREAD *thread = info->thread;
11+
FBTHREADFLAGS threadFlags;
12+
13+
FB_TLSGETCTX( FBTHREAD )->self = thread;
1014

1115
/* call the user thread */
1216
info->proc( info->param );
17+
1318
free( info );
1419

1520
/* free mem */
1621
fb_TlsFreeCtxTb( );
22+
threadFlags = fb_AtomicSetThreadFlags( &thread->flags, FBTHREAD_EXITED );
23+
24+
if( threadFlags & FBTHREAD_DETACHED ) {
25+
free( thread );
26+
}
1727

1828
/* don't return NULL or exit() will be called */
1929
return (void *)1;
@@ -38,6 +48,8 @@ FBCALL FBTHREAD *fb_ThreadCreate( FB_THREADPROC proc, void *param, ssize_t stack
3848

3949
info->proc = proc;
4050
info->param = param;
51+
info->thread = thread;
52+
thread->flags = 0;
4153

4254
if( pthread_attr_init( &tattr ) ) {
4355
free( thread );
@@ -64,10 +76,19 @@ FBCALL FBTHREAD *fb_ThreadCreate( FB_THREADPROC proc, void *param, ssize_t stack
6476

6577
FBCALL void fb_ThreadWait( FBTHREAD *thread )
6678
{
67-
if( thread == NULL )
79+
/* A wait for the main thread or ourselves will never end
80+
also, if we've been detached, we've nothing to wait on
81+
*/
82+
if(
83+
( thread == NULL ) ||
84+
( ( thread->flags & ( FBTHREAD_MAIN | FBTHREAD_DETACHED ) ) != 0 ) ||
85+
( thread == FB_TLSGETCTX( FBTHREAD )->self )
86+
) {
6887
return;
88+
}
6989

7090
pthread_join( thread->id, NULL );
7191

7292
free( thread );
7393
}
94+

src/rtlib/unix/thread_detach.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33

44
FBCALL void fb_ThreadDetach( FBTHREAD *thread )
55
{
6-
if( thread == NULL )
6+
FBTHREADFLAGS flags;
7+
8+
if( thread == NULL || ( thread->flags & FBTHREAD_MAIN ) )
79
return;
810

11+
flags = fb_AtomicSetThreadFlags( &thread->flags, FBTHREAD_DETACHED );
912
pthread_detach( thread->id );
10-
11-
free( thread );
13+
/* This thread has already exited, so clean up */
14+
if( flags & FBTHREAD_EXITED ) {
15+
free( thread );
16+
}
1217
}

0 commit comments

Comments
 (0)