FreeBASIC  0.91.0
thread_call.c
Go to the documentation of this file.
1 
22 #include "fb.h"
23 
24 #if defined DISABLE_FFI || defined HOST_DOS || (!defined HOST_X86 && !defined HOST_X86_64)
25 
26 FBTHREAD *fb_ThreadCall( void *proc, int abi, ssize_t stack_size, int num_args, ... )
27 {
28  return NULL;
29 }
30 
31 #else
32 
33 #include <ffi.h>
34 
35 #define FB_THREADCALL_MAX_ELEMS 1024
36 
37 typedef struct _FBTHREADCALL
38 {
39  void *proc;
40  int abi;
41  int num_args;
42  ffi_type **ffi_arg_types;
43  void **values;
44 } FBTHREADCALL;
45 
46 /* mirrored in compiler/rtl.bi */
47 enum {
62 };
63 
64 static void freeStruct( ffi_type *arg )
65 {
66  int i = 0;
67  ffi_type **elem = arg->elements;
68 
69  while( *elem != NULL )
70  {
71  /* cap element count to limit buffer overrun */
72  if ( i >= FB_THREADCALL_MAX_ELEMS )
73  break;
74 
75  /* free embedded types */
76  if( (*elem)->type == FFI_TYPE_STRUCT )
77  freeStruct( *elem );
78 
79  elem++;
80  i++;
81  }
82 
83  free( arg->elements );
84  free( arg );
85 }
86 
87 static ffi_type *getArgument( va_list *args_list );
88 
89 static ffi_type *getStruct( va_list *args_list )
90 {
91  int num_elems = va_arg( (*args_list), int );
92  int i, j;
93 
94  /* prepare type */
95  ffi_type *ffi_arg = (ffi_type *)malloc( sizeof( ffi_type ) );
96  ffi_arg->size = 0;
97  ffi_arg->alignment = 0;
98  ffi_arg->type = FFI_TYPE_STRUCT;
99  ffi_arg->elements =
100  (ffi_type **)malloc( sizeof( ffi_type * ) * ( num_elems + 1 ) );
101  ffi_arg->elements[num_elems] = NULL;
102 
103  /* scan elements */
104  for( i=0; i<num_elems; i++ )
105  {
106  ffi_arg->elements[i] = getArgument( args_list );
107  if( ffi_arg->elements[i] == NULL )
108  {
109  /* error, free memory and return NULL */
110  for( j=0; j<i; j++ )
111  {
112  if( ffi_arg->elements[j]->type == FFI_TYPE_STRUCT )
113  freeStruct( ffi_arg );
114  }
115  free( ffi_arg->elements );
116  free( ffi_arg );
117  return NULL;
118  }
119  }
120 
121  return ffi_arg;
122 }
123 
124 static ffi_type *getArgument( va_list *args_list )
125 {
126  int arg_type = va_arg( (*args_list), int );
127  switch( arg_type )
128  {
129  case FB_THREADCALL_INT8: return &ffi_type_sint8;
130  case FB_THREADCALL_UINT8: return &ffi_type_uint8;
131  case FB_THREADCALL_INT16: return &ffi_type_sint16;
132  case FB_THREADCALL_UINT16: return &ffi_type_uint16;
133  case FB_THREADCALL_INT32: return &ffi_type_sint32;
134  case FB_THREADCALL_UINT32: return &ffi_type_uint32;
135  case FB_THREADCALL_INT64: return &ffi_type_sint64;
136  case FB_THREADCALL_UINT64: return &ffi_type_uint64;
137  case FB_THREADCALL_FLOAT32: return &ffi_type_float;
138  case FB_THREADCALL_FLOAT64: return &ffi_type_double;
139  case FB_THREADCALL_STRUCT: return getStruct( args_list );
140  case FB_THREADCALL_PTR: return &ffi_type_pointer;
141  default:
142  return NULL;
143  }
144 }
145 
146 static FBCALL void threadproc( void *param );
147 
148 FBTHREAD *fb_ThreadCall( void *proc, int abi, ssize_t stack_size, int num_args, ... )
149 {
150  ffi_type **ffi_args;
151  void **values;
152  FBTHREADCALL *param;
153  int i, j;
154 
155  /* initialize lists and arrays */
156  ffi_args = (ffi_type **)malloc( sizeof( ffi_type * ) * num_args );
157  values = (void **)malloc( sizeof( void * ) * num_args );
158  va_list args_list;
159  va_start(args_list, num_args);
160 
161  /* scan arguments and values from var_args */
162  for( i=0; i<num_args; i++ )
163  {
164  ffi_args[i] = getArgument( &args_list );
165  if( ffi_args[i] == NULL )
166  {
167  /* error, free all memory allocated up to this point */
168  for( j=0; j<i; j++ )
169  {
170  if( ffi_args[i]->type == FFI_TYPE_STRUCT )
171  freeStruct( ffi_args[i] );
172  }
173  return NULL;
174  }
175  values[i] = va_arg( args_list, void * );
176  }
177  va_end( args_list );
178 
179  /* pack into thread parameter */
180  param = malloc( sizeof( FBTHREADCALL ) );
181  param->proc = proc;
182  param->abi = abi;
183  param->num_args = num_args;
184  param->ffi_arg_types = ffi_args;
185  param->values = values;
186 
187  /* actually start thread */
188  return fb_ThreadCreate( threadproc, (void *)param, stack_size );
189 }
190 
191 static FBCALL void threadproc( void *param )
192 {
193  FBTHREADCALL *info = ( FBTHREADCALL * )param;
194  ffi_status status = FFI_OK;
195  ffi_abi abi = -1;
196  ffi_cif cif;
197  int i;
198 
199 #ifdef HOST_X86_64
200  abi = FFI_DEFAULT_ABI;
201 #else
202  /* check calling convention */
203  if( info->abi == FB_THREADCALL_CDECL )
204  abi = FFI_SYSV;
205 #ifdef HOST_WIN32
206  else if( info->abi == FB_THREADCALL_STDCALL )
207  abi = FFI_STDCALL;
208 #endif
209  else
210  status = ~FFI_OK;
211 
212  /* prep FFI call interface */
213  if( status == FFI_OK )
214 #endif
215  status = ffi_prep_cif(
216  &cif, // handle
217  abi, // ABI (CDECL or STDCALL on x86, host default on x86_64)
218  info->num_args, // number of arguments
219  &ffi_type_void, // return type
220  info->ffi_arg_types // argument types
221  );
222 
223  /* execute */
224  if( status == FFI_OK )
225  ffi_call( &cif, FFI_FN( info->proc ), NULL, info->values );
226 
227 
228  /* free memory and exit */
229  for( i=0; i<info->num_args; i++ )
230  {
231  if( info->ffi_arg_types[i]->type == FFI_TYPE_STRUCT )
232  freeStruct( info->ffi_arg_types[i] );
233  }
234  free( info->values );
235  free( info->ffi_arg_types );
236  free( info );
237 }
238 
239 #endif