/* emacs-module.c - Module loading and runtime implementation
-Copyright (C) 2015 Free Software Foundation, Inc.
+Copyright (C) 2015-2016 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs 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 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
\f
/* Feature tests. */
-/* True if __attribute__ ((cleanup (...))) works, false otherwise. */
-#ifdef HAVE_VAR_ATTRIBUTE_CLEANUP
+#if __has_attribute (cleanup)
enum { module_has_cleanup = true };
#else
enum { module_has_cleanup = false };
/* Handle to the main thread. Used to verify that modules call us in
the right thread. */
-#ifdef HAVE_THREADS_H
-# include <threads.h>
-static thrd_t main_thread;
-#elif defined HAVE_PTHREAD
+#ifdef HAVE_PTHREAD
# include <pthread.h>
static pthread_t main_thread;
#elif defined WINDOWSNT
static DWORD main_thread;
#endif
-\f
-/* Memory management. */
-
-/* An `emacs_value' is just a pointer to a structure holding an
- internal Lisp object. */
-struct emacs_value_tag { Lisp_Object v; };
-
-/* Local value objects use a simple fixed-sized block allocation
- scheme without explicit deallocation. All local values are
- deallocated when the lifetime of their environment ends. Keep
- track of a current frame from which new values are allocated,
- appending further dynamically-allocated frames if necessary. */
-
-enum { value_frame_size = 512 };
-
-/* A block from which `emacs_value' object can be allocated. */
-struct emacs_value_frame
-{
- /* Storage for values. */
- struct emacs_value_tag objects[value_frame_size];
-
- /* Index of the next free value in `objects'. */
- int offset;
-
- /* Pointer to next frame, if any. */
- struct emacs_value_frame *next;
-};
-
-/* A structure that holds an initial frame (so that the first local
- values require no dynamic allocation) and keeps track of the
- current frame. */
-static struct emacs_value_storage
-{
- struct emacs_value_frame initial;
- struct emacs_value_frame *current;
-} global_storage;
+/* True if Lisp_Object and emacs_value have the same representation.
+ This is typically true unless WIDE_EMACS_INT. In practice, having
+ the same sizes and alignments and maximums should be a good enough
+ proxy for equality of representation. */
+enum
+ {
+ plain_values
+ = (sizeof (Lisp_Object) == sizeof (emacs_value)
+ && alignof (Lisp_Object) == alignof (emacs_value)
+ && INTPTR_MAX == EMACS_INT_MAX)
+ };
+
+/* Function prototype for module user-pointer finalizers. These
+ should not throw C++ exceptions, so emacs-module.h declares the
+ corresponding interfaces with EMACS_NOEXCEPT. There is only C code
+ in this module, though, so this constraint is not enforced here. */
+typedef void (*emacs_finalizer_function) (void *);
\f
/* Private runtime and environment members. */
/* Dedicated storage for non-local exit symbol and data so that
storage is always available for them, even in an out-of-memory
situation. */
- struct emacs_value_tag non_local_exit_symbol, non_local_exit_data;
-
- struct emacs_value_storage storage;
+ Lisp_Object non_local_exit_symbol, non_local_exit_data;
};
/* The private parts of an `emacs_runtime' object contain the initial
struct emacs_runtime_private
{
/* FIXME: Ideally, we would just define "struct emacs_runtime_private"
- * as a synonym of "emacs_env", but I don't know how to do that in C. */
+ as a synonym of "emacs_env", but I don't know how to do that in C. */
emacs_env pub;
};
\f
static Lisp_Object module_format_fun_env (const struct module_fun_env *);
static Lisp_Object value_to_lisp (emacs_value);
-static emacs_value allocate_emacs_value (emacs_env *, struct emacs_value_storage *, Lisp_Object);
-static emacs_value lisp_to_value (emacs_env *, Lisp_Object);
+static emacs_value lisp_to_value (Lisp_Object);
static enum emacs_funcall_exit module_non_local_exit_check (emacs_env *);
static void check_main_thread (void);
static void finalize_environment (struct emacs_env_private *);
static void module_reset_handlerlist (const int *);
static void module_wrong_type (emacs_env *, Lisp_Object, Lisp_Object);
+/* We used to return NULL when emacs_value was a different type from
+ Lisp_Object, but nowadays we just use Qnil instead. Although they
+ happen to be the same thing in the current implementation, module
+ code should not assume this. */
+verify (NIL_IS_ZERO);
+static emacs_value const module_nil = 0;
\f
/* Convenience macros for non-local exit handling. */
+/* FIXME: The following implementation for non-local exit handling
+ does not support recovery from stack overflow, see sysdep.c. */
+
/* Emacs uses setjmp and longjmp for non-local exits, but
module frames cannot be skipped because they are in general
not prepared for long jumps (e.g., the behavior in C++ is undefined
static emacs_value
module_make_global_ref (emacs_env *env, emacs_value ref)
{
- MODULE_FUNCTION_BEGIN (NULL);
+ MODULE_FUNCTION_BEGIN (module_nil);
struct Lisp_Hash_Table *h = XHASH_TABLE (Vmodule_refs_hash);
Lisp_Object new_obj = value_to_lisp (ref);
EMACS_UINT hashcode;
if (refcount > MOST_POSITIVE_FIXNUM)
{
module_non_local_exit_signal_1 (env, Qoverflow_error, Qnil);
- return NULL;
+ return module_nil;
}
value = make_natnum (refcount);
set_hash_value_slot (h, i, value);
hash_put (h, new_obj, make_natnum (1), hashcode);
}
- return allocate_emacs_value (env, &global_storage, new_obj);
+ return lisp_to_value (new_obj);
}
static void
struct emacs_env_private *p = env->private_members;
if (p->pending_non_local_exit != emacs_funcall_exit_return)
{
- *sym = &p->non_local_exit_symbol;
- *data = &p->non_local_exit_data;
+ /* FIXME: lisp_to_value can exit non-locally. */
+ *sym = lisp_to_value (p->non_local_exit_symbol);
+ *data = lisp_to_value (p->non_local_exit_data);
}
return p->pending_non_local_exit;
}
emacs_subr subr, const char *documentation,
void *data)
{
- MODULE_FUNCTION_BEGIN (NULL);
+ MODULE_FUNCTION_BEGIN (module_nil);
if (! (0 <= min_arity
&& (max_arity < 0
? code_convert_string_norecord (build_unibyte_string (documentation),
Qutf_8, false)
: Qnil);
+ /* FIXME: Use a bytecompiled object, or even better a subr. */
Lisp_Object ret = list4 (Qlambda,
list2 (Qand_rest, Qargs),
doc,
- list3 (Qinternal_module_call,
+ list4 (Qapply,
+ list2 (Qfunction, Qinternal_module_call),
envobj,
Qargs));
- return lisp_to_value (env, ret);
+ return lisp_to_value (ret);
}
static emacs_value
module_funcall (emacs_env *env, emacs_value fun, ptrdiff_t nargs,
emacs_value args[])
{
- MODULE_FUNCTION_BEGIN (NULL);
+ MODULE_FUNCTION_BEGIN (module_nil);
/* Make a new Lisp_Object array starting with the function as the
first arg, because that's what Ffuncall takes. */
newargs[0] = value_to_lisp (fun);
for (ptrdiff_t i = 0; i < nargs; i++)
newargs[1 + i] = value_to_lisp (args[i]);
- emacs_value result = lisp_to_value (env, Ffuncall (nargs + 1, newargs));
+ emacs_value result = lisp_to_value (Ffuncall (nargs + 1, newargs));
SAFE_FREE ();
return result;
}
static emacs_value
module_intern (emacs_env *env, const char *name)
{
- MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, intern (name));
+ MODULE_FUNCTION_BEGIN (module_nil);
+ return lisp_to_value (intern (name));
}
static emacs_value
module_type_of (emacs_env *env, emacs_value value)
{
- MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, Ftype_of (value_to_lisp (value)));
+ MODULE_FUNCTION_BEGIN (module_nil);
+ return lisp_to_value (Ftype_of (value_to_lisp (value)));
}
static bool
static emacs_value
module_make_integer (emacs_env *env, intmax_t n)
{
- MODULE_FUNCTION_BEGIN (NULL);
+ MODULE_FUNCTION_BEGIN (module_nil);
if (! (MOST_NEGATIVE_FIXNUM <= n && n <= MOST_POSITIVE_FIXNUM))
{
module_non_local_exit_signal_1 (env, Qoverflow_error, Qnil);
- return NULL;
+ return module_nil;
}
- return lisp_to_value (env, make_number (n));
+ return lisp_to_value (make_number (n));
}
static double
static emacs_value
module_make_float (emacs_env *env, double d)
{
- MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_float (d));
+ MODULE_FUNCTION_BEGIN (module_nil);
+ return lisp_to_value (make_float (d));
}
static bool
static emacs_value
module_make_string (emacs_env *env, const char *str, ptrdiff_t length)
{
- MODULE_FUNCTION_BEGIN (NULL);
+ MODULE_FUNCTION_BEGIN (module_nil);
if (length > STRING_BYTES_BOUND)
{
module_non_local_exit_signal_1 (env, Qoverflow_error, Qnil);
- return NULL;
+ return module_nil;
}
Lisp_Object lstr = make_unibyte_string (str, length);
- return lisp_to_value (env,
- code_convert_string_norecord (lstr, Qutf_8, false));
+ return lisp_to_value (code_convert_string_norecord (lstr, Qutf_8, false));
}
static emacs_value
module_make_user_ptr (emacs_env *env, emacs_finalizer_function fin, void *ptr)
{
- MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_user_ptr (fin, ptr));
+ MODULE_FUNCTION_BEGIN (module_nil);
+ return lisp_to_value (make_user_ptr (fin, ptr));
}
static void *
static void
module_set_user_ptr (emacs_env *env, emacs_value uptr, void *ptr)
{
- // FIXME: This function should return bool because it can fail.
+ /* FIXME: This function should return bool because it can fail. */
MODULE_FUNCTION_BEGIN ();
check_main_thread ();
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
module_set_user_finalizer (emacs_env *env, emacs_value uptr,
emacs_finalizer_function fin)
{
- // FIXME: This function should return bool because it can fail.
+ /* FIXME: This function should return bool because it can fail. */
MODULE_FUNCTION_BEGIN ();
Lisp_Object lisp = value_to_lisp (uptr);
if (! USER_PTRP (lisp))
static void
module_vec_set (emacs_env *env, emacs_value vec, ptrdiff_t i, emacs_value val)
{
- // FIXME: This function should return bool because it can fail.
+ /* FIXME: This function should return bool because it can fail. */
MODULE_FUNCTION_BEGIN ();
Lisp_Object lvec = value_to_lisp (vec);
if (! VECTORP (lvec))
static emacs_value
module_vec_get (emacs_env *env, emacs_value vec, ptrdiff_t i)
{
- MODULE_FUNCTION_BEGIN (NULL);
+ MODULE_FUNCTION_BEGIN (module_nil);
Lisp_Object lvec = value_to_lisp (vec);
if (! VECTORP (lvec))
{
module_wrong_type (env, Qvectorp, lvec);
- return NULL;
+ return module_nil;
}
if (! (0 <= i && i < ASIZE (lvec)))
{
module_args_out_of_range (env, lvec, make_number (i));
else
module_non_local_exit_signal_1 (env, Qoverflow_error, Qnil);
- return NULL;
+ return module_nil;
}
- return lisp_to_value (env, AREF (lvec, i));
+ return lisp_to_value (AREF (lvec, i));
}
static ptrdiff_t
module_vec_size (emacs_env *env, emacs_value vec)
{
- // FIXME: Return a sentinel value (e.g., -1) on error.
+ /* FIXME: Return a sentinel value (e.g., -1) on error. */
MODULE_FUNCTION_BEGIN (0);
Lisp_Object lvec = value_to_lisp (vec);
if (! VECTORP (lvec))
return Qt;
}
-DEFUN ("internal--module-call", Finternal_module_call, Sinternal_module_call, 2, 2, 0,
+DEFUN ("internal--module-call", Finternal_module_call, Sinternal_module_call, 1, MANY, 0,
doc: /* Internal function to call a module function.
ENVOBJ is a save pointer to a module_fun_env structure.
-ARGLIST is a list of arguments passed to SUBRPTR, or nil. */)
- (Lisp_Object envobj, Lisp_Object arglist)
+ARGLIST is a list of arguments passed to SUBRPTR.
+usage: (module-call ENVOBJ &rest ARGLIST) */)
+ (ptrdiff_t nargs, Lisp_Object *arglist)
{
+ Lisp_Object envobj = arglist[0];
+ /* FIXME: Rather than use a save_value, we should create a new object type.
+ Making save_value visible to Lisp is wrong. */
CHECK_TYPE (SAVE_VALUEP (envobj), Qsave_value_p, envobj);
struct Lisp_Save_Value *save_value = XSAVE_VALUE (envobj);
CHECK_TYPE (save_type (save_value, 0) == SAVE_POINTER, Qsave_pointer_p, envobj);
- if (!NILP (arglist))
- CHECK_CONS (arglist);
+ /* FIXME: We have no reason to believe that XSAVE_POINTER (envobj, 0)
+ is a module_fun_env pointer. If some other part of Emacs also
+ exports save_value objects to Elisp, than we may be getting here this
+ other kind of save_value which will likely hold something completely
+ different in this field. */
struct module_fun_env *envptr = XSAVE_POINTER (envobj, 0);
- EMACS_INT len = XFASTINT (Flength (arglist));
+ EMACS_INT len = nargs - 1;
eassume (0 <= envptr->min_arity);
if (! (envptr->min_arity <= len
&& len <= (envptr->max_arity < 0 ? PTRDIFF_MAX : envptr->max_arity)))
struct emacs_env_private priv;
initialize_environment (&pub, &priv);
- emacs_value *args = xnmalloc (len, sizeof *args);
-
- for (ptrdiff_t i = 0; i < len; i++)
+ USE_SAFE_ALLOCA;
+ emacs_value *args;
+ if (plain_values)
+ args = (emacs_value *) arglist + 1;
+ else
{
- args[i] = lisp_to_value (&pub, XCAR (arglist));
- if (! args[i])
- memory_full (sizeof *args[i]);
- arglist = XCDR (arglist);
+ args = SAFE_ALLOCA (len * sizeof *args);
+ for (ptrdiff_t i = 0; i < len; i++)
+ args[i] = lisp_to_value (arglist[i + 1]);
}
emacs_value ret = envptr->subr (&pub, len, args, envptr->data);
- xfree (args);
+ SAFE_FREE ();
eassert (&priv == pub.private_members);
{
case emacs_funcall_exit_return:
finalize_environment (&priv);
- if (ret == NULL)
- xsignal1 (Qinvalid_module_call, module_format_fun_env (envptr));
return value_to_lisp (ret);
case emacs_funcall_exit_signal:
{
- Lisp_Object symbol = value_to_lisp (&priv.non_local_exit_symbol);
- Lisp_Object data = value_to_lisp (&priv.non_local_exit_data);
+ Lisp_Object symbol = priv.non_local_exit_symbol;
+ Lisp_Object data = priv.non_local_exit_data;
finalize_environment (&priv);
xsignal (symbol, data);
}
case emacs_funcall_exit_throw:
{
- Lisp_Object tag = value_to_lisp (&priv.non_local_exit_symbol);
- Lisp_Object value = value_to_lisp (&priv.non_local_exit_data);
+ Lisp_Object tag = priv.non_local_exit_symbol;
+ Lisp_Object value = priv.non_local_exit_data;
finalize_environment (&priv);
Fthrow (tag, value);
}
static void
check_main_thread (void)
{
-#ifdef HAVE_THREADS_H
- eassert (thrd_equal (thdr_current (), main_thread));
-#elif defined HAVE_PTHREAD
+#ifdef HAVE_PTHREAD
eassert (pthread_equal (pthread_self (), main_thread));
#elif defined WINDOWSNT
eassert (GetCurrentThreadId () == main_thread);
if (p->pending_non_local_exit == emacs_funcall_exit_return)
{
p->pending_non_local_exit = emacs_funcall_exit_signal;
- p->non_local_exit_symbol.v = sym;
- p->non_local_exit_data.v = data;
+ p->non_local_exit_symbol = sym;
+ p->non_local_exit_data = data;
}
}
if (p->pending_non_local_exit == emacs_funcall_exit_return)
{
p->pending_non_local_exit = emacs_funcall_exit_throw;
- p->non_local_exit_symbol.v = tag;
- p->non_local_exit_data.v = value;
+ p->non_local_exit_symbol = tag;
+ p->non_local_exit_data = value;
}
}
\f
/* Value conversion. */
-/* Convert an `emacs_value' to the corresponding internal object.
- Never fails. */
+/* Unique Lisp_Object used to mark those emacs_values which are really
+ just containers holding a Lisp_Object that does not fit as an emacs_value,
+ either because it is an integer out of range, or is not properly aligned.
+ Used only if !plain_values. */
+static Lisp_Object ltv_mark;
+
+/* Convert V to the corresponding internal object O, such that
+ V == lisp_to_value_bits (O). Never fails. */
static Lisp_Object
-value_to_lisp (emacs_value v)
+value_to_lisp_bits (emacs_value v)
{
- return v->v;
-}
+ intptr_t i = (intptr_t) v;
+ if (plain_values || USE_LSB_TAG)
+ return XIL (i);
+
+ /* With wide EMACS_INT and when tag bits are the most significant,
+ reassembling integers differs from reassembling pointers in two
+ ways. First, save and restore the least-significant bits of the
+ integer, not the most-significant bits. Second, sign-extend the
+ integer when restoring, but zero-extend pointers because that
+ makes TAG_PTR faster. */
+
+ EMACS_UINT tag = i & (GCALIGNMENT - 1);
+ EMACS_UINT untagged = i - tag;
+ switch (tag)
+ {
+ case_Lisp_Int:
+ {
+ bool negative = tag & 1;
+ EMACS_UINT sign_extension
+ = negative ? VALMASK & ~(INTPTR_MAX >> INTTYPEBITS): 0;
+ uintptr_t u = i;
+ intptr_t all_but_sign = u >> GCTYPEBITS;
+ untagged = sign_extension + all_but_sign;
+ break;
+ }
+ }
-/* Convert an internal object to an `emacs_value'. Allocate storage
- from the environment; return NULL if allocation fails. */
-static emacs_value
-lisp_to_value (emacs_env *env, Lisp_Object o)
-{
- struct emacs_env_private *p = env->private_members;
- if (p->pending_non_local_exit != emacs_funcall_exit_return)
- return NULL;
- return allocate_emacs_value (env, &p->storage, o);
+ return XIL ((tag << VALBITS) + untagged);
}
-\f
-/* Memory management. */
-
-/* Must be called for each frame before it can be used for allocation. */
-static void
-initialize_frame (struct emacs_value_frame *frame)
+/* If V was computed from lisp_to_value (O), then return O.
+ Exits non-locally only if the stack overflows. */
+static Lisp_Object
+value_to_lisp (emacs_value v)
{
- frame->offset = 0;
- frame->next = NULL;
+ Lisp_Object o = value_to_lisp_bits (v);
+ if (! plain_values && CONSP (o) && EQ (XCDR (o), ltv_mark))
+ o = XCAR (o);
+ return o;
}
-/* Must be called for any storage object before it can be used for
- allocation. */
-static void
-initialize_storage (struct emacs_value_storage *storage)
+/* Attempt to convert O to an emacs_value. Do not do any checking or
+ or allocate any storage; the caller should prevent or detect
+ any resulting bit pattern that is not a valid emacs_value. */
+static emacs_value
+lisp_to_value_bits (Lisp_Object o)
{
- initialize_frame (&storage->initial);
- storage->current = &storage->initial;
-}
+ EMACS_UINT u = XLI (o);
-/* Must be called for any initialized storage object before its
- lifetime ends. Free all dynamically-allocated frames. */
-static void
-finalize_storage (struct emacs_value_storage *storage)
-{
- struct emacs_value_frame *next = storage->initial.next;
- while (next != NULL)
- {
- struct emacs_value_frame *current = next;
- next = current->next;
- free (current);
- }
+ /* Compress U into the space of a pointer, possibly losing information. */
+ uintptr_t p = (plain_values || USE_LSB_TAG
+ ? u
+ : (INTEGERP (o) ? u << VALBITS : u & VALMASK) + XTYPE (o));
+ return (emacs_value) p;
}
-/* Allocate a new value from STORAGE and stores OBJ in it. Return
- NULL if allocation fails and use ENV for non local exit reporting. */
+#ifndef HAVE_STRUCT_ATTRIBUTE_ALIGNED
+enum { HAVE_STRUCT_ATTRIBUTE_ALIGNED = 0 };
+#endif
+
+/* Convert O to an emacs_value. Allocate storage if needed; this can
+ signal if memory is exhausted. Must be an injective function. */
static emacs_value
-allocate_emacs_value (emacs_env *env, struct emacs_value_storage *storage,
- Lisp_Object obj)
+lisp_to_value (Lisp_Object o)
{
- eassert (storage->current);
- eassert (storage->current->offset < value_frame_size);
- eassert (! storage->current->next);
- if (storage->current->offset == value_frame_size - 1)
- {
- storage->current->next = malloc (sizeof *storage->current->next);
- if (! storage->current->next)
- {
- module_out_of_memory (env);
- return NULL;
- }
- initialize_frame (storage->current->next);
- storage->current = storage->current->next;
- }
- emacs_value value = storage->current->objects + storage->current->offset;
- value->v = obj;
- ++storage->current->offset;
- return value;
-}
+ emacs_value v = lisp_to_value_bits (o);
-/* Mark all objects allocated from local environments so that they
- don't get garbage-collected. */
-void
-mark_modules (void)
-{
- for (Lisp_Object tem = Vmodule_environments; CONSP (tem); tem = XCDR (tem))
+ if (! EQ (o, value_to_lisp_bits (v)))
{
- struct emacs_env_private *priv = XSAVE_POINTER (tem, 0);
- for (struct emacs_value_frame *frame = &priv->storage.initial;
- frame != NULL;
- frame = frame->next)
- for (int i = 0; i < frame->offset; ++i)
- mark_object (frame->objects[i].v);
+ /* Package the incompressible object pointer inside a pair
+ that is compressible. */
+ Lisp_Object pair = Fcons (o, ltv_mark);
+
+ if (! HAVE_STRUCT_ATTRIBUTE_ALIGNED)
+ {
+ /* Keep calling Fcons until it returns a compressible pair.
+ This shouldn't take long. */
+ while ((intptr_t) XCONS (pair) & (GCALIGNMENT - 1))
+ pair = Fcons (o, pair);
+
+ /* Plant the mark. The garbage collector will eventually
+ reclaim any just-allocated incompressible pairs. */
+ XSETCDR (pair, ltv_mark);
+ }
+
+ v = (emacs_value) ((intptr_t) XCONS (pair) + Lisp_Cons);
}
+
+ eassert (EQ (o, value_to_lisp (v)));
+ return v;
}
\f
initialize_environment (emacs_env *env, struct emacs_env_private *priv)
{
priv->pending_non_local_exit = emacs_funcall_exit_return;
- initialize_storage (&priv->storage);
env->size = sizeof *env;
env->private_members = priv;
env->make_global_ref = module_make_global_ref;
env->vec_set = module_vec_set;
env->vec_get = module_vec_get;
env->vec_size = module_vec_size;
- Vmodule_environments = Fcons (make_save_ptr (priv), Vmodule_environments);
+ Vmodule_environments = Fcons (make_save_ptr (env), Vmodule_environments);
}
/* Must be called before the lifetime of the environment object
static void
finalize_environment (struct emacs_env_private *env)
{
- finalize_storage (&env->storage);
Vmodule_environments = XCDR (Vmodule_environments);
}
void
syms_of_module (void)
{
+ if (!plain_values)
+ ltv_mark = Fcons (Qnil, Qnil);
+ eassert (NILP (value_to_lisp (module_nil)));
+
DEFSYM (Qmodule_refs_hash, "module-refs-hash");
DEFVAR_LISP ("module-refs-hash", Vmodule_refs_hash,
doc: /* Module global reference table. */);
Fput (Qinvalid_arity, Qerror_message,
build_pure_c_string ("Invalid function arity"));
- initialize_storage (&global_storage);
-
/* Unintern `module-refs-hash' because it is internal-only and Lisp
code or modules should not access it. */
Funintern (Qmodule_refs_hash, Qnil);
{
/* It is not guaranteed that dynamic initializers run in the main thread,
therefore detect the main thread here. */
-#ifdef HAVE_THREADS_H
- main_thread = thrd_current ();
-#elif defined HAVE_PTHREAD
+#ifdef HAVE_PTHREAD
main_thread = pthread_self ();
#elif defined WINDOWSNT
/* The 'main' function already recorded the main thread's thread ID,