mark_fringe_data ();
#endif
-#ifdef HAVE_MODULES
- mark_modules ();
-#endif
-
/* Everything is now marked, except for the data in font caches,
undo lists, and finalizers. The first two are compacted by
removing an items which aren't reachable otherwise. */
static emacs_env *initialize_environment (emacs_env *,
struct emacs_env_private *);
static void finalize_environment (emacs_env *);
-static void finalize_environment_unwind (void *);
-static void finalize_runtime_unwind (void *);
static void module_handle_nonlocal_exit (emacs_env *, enum nonlocal_exit,
Lisp_Object);
static void module_non_local_exit_signal_1 (emacs_env *,
}
}
-/* Live runtime and environment objects, for assertions. */
-static Lisp_Object Vmodule_runtimes;
-static Lisp_Object Vmodule_environments;
-
DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0,
doc: /* Load module FILE. */)
(Lisp_Object file)
rt->private_members = &rt_priv;
rt->get_environment = module_get_environment;
- Vmodule_runtimes = Fcons (make_mint_ptr (rt), Vmodule_runtimes);
ptrdiff_t count = SPECPDL_INDEX ();
- record_unwind_protect_ptr (finalize_runtime_unwind, rt);
+ record_unwind_protect_module (SPECPDL_MODULE_RUNTIME, rt);
+ record_unwind_protect_module (SPECPDL_MODULE_ENVIRONMENT, rt_priv.env);
int r = module_init (rt);
struct emacs_env_private priv;
emacs_env *env = initialize_environment (&pub, &priv);
ptrdiff_t count = SPECPDL_INDEX ();
- record_unwind_protect_ptr (finalize_environment_unwind, env);
+ record_unwind_protect_module (SPECPDL_MODULE_ENVIRONMENT, env);
USE_SAFE_ALLOCA;
emacs_value *args = nargs > 0 ? SAFE_ALLOCA (nargs * sizeof *args) : NULL;
if (! module_assertions)
return;
ptrdiff_t count = 0;
- for (Lisp_Object tail = Vmodule_runtimes; CONSP (tail); tail = XCDR (tail))
- {
- if (xmint_pointer (XCAR (tail)) == runtime)
- return;
- ++count;
- }
+ for (const union specbinding *pdl = specpdl; pdl != specpdl_ptr; ++pdl)
+ if (pdl->kind == SPECPDL_MODULE_RUNTIME)
+ {
+ if (pdl->unwind_ptr.arg == runtime)
+ return;
+ ++count;
+ }
module_abort ("Runtime pointer not found in list of %"pD"d runtimes",
count);
}
if (! module_assertions)
return;
ptrdiff_t count = 0;
- for (Lisp_Object tail = Vmodule_environments; CONSP (tail);
- tail = XCDR (tail))
- {
- if (xmint_pointer (XCAR (tail)) == env)
- return;
- ++count;
- }
+ for (const union specbinding *pdl = specpdl; pdl != specpdl_ptr; ++pdl)
+ if (pdl->kind == SPECPDL_MODULE_ENVIRONMENT)
+ {
+ if (pdl->unwind_ptr.arg == env)
+ return;
+ ++count;
+ }
module_abort ("Environment pointer not found in list of %"pD"d environments",
count);
}
environments. */
ptrdiff_t num_environments = 0;
ptrdiff_t num_values = 0;
- for (Lisp_Object environments = Vmodule_environments;
- CONSP (environments); environments = XCDR (environments))
- {
- emacs_env *env = xmint_pointer (XCAR (environments));
- struct emacs_env_private *priv = env->private_members;
- /* The value might be one of the nonlocal exit values. Note
- that we don't check whether a nonlocal exit is currently
- pending, because the module might have cleared the flag
- in the meantime. */
- if (&priv->non_local_exit_symbol == v
- || &priv->non_local_exit_data == v)
- goto ok;
- if (value_storage_contains_p (&priv->storage, v, &num_values))
- goto ok;
- ++num_environments;
- }
+ for (const union specbinding *pdl = specpdl; pdl != specpdl_ptr; ++pdl)
+ if (pdl->kind == SPECPDL_MODULE_ENVIRONMENT)
+ {
+ const emacs_env *env = pdl->unwind_ptr.arg;
+ struct emacs_env_private *priv = env->private_members;
+ /* The value might be one of the nonlocal exit values. Note
+ that we don't check whether a nonlocal exit is currently
+ pending, because the module might have cleared the flag
+ in the meantime. */
+ if (&priv->non_local_exit_symbol == v
+ || &priv->non_local_exit_data == v)
+ goto ok;
+ if (value_storage_contains_p (&priv->storage, v, &num_values))
+ goto ok;
+ ++num_environments;
+ }
/* Also check global values. */
if (module_global_reference_p (v, &num_values))
goto ok;
/* Mark all objects allocated from local environments so that they
don't get garbage-collected. */
void
-mark_modules (void)
+mark_module_environment (void *ptr)
{
- for (Lisp_Object tem = Vmodule_environments; CONSP (tem); tem = XCDR (tem))
- {
- emacs_env *env = xmint_pointer (XCAR (tem));
- struct emacs_env_private *priv = env->private_members;
- 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);
- }
+ emacs_env *env = ptr;
+ struct emacs_env_private *priv = env->private_members;
+ 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);
}
\f
env->set_function_finalizer = module_set_function_finalizer;
env->open_channel = module_open_channel;
env->make_interactive = module_make_interactive;
- Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments);
return env;
}
finalize_environment (emacs_env *env)
{
finalize_storage (&env->private_members->storage);
- eassert (xmint_pointer (XCAR (Vmodule_environments)) == env);
- Vmodule_environments = XCDR (Vmodule_environments);
}
-static void
+void
finalize_environment_unwind (void *env)
{
finalize_environment (env);
}
-static void
+void
finalize_runtime_unwind (void *raw_ert)
{
struct emacs_runtime *ert = raw_ert;
- eassert (xmint_pointer (XCAR (Vmodule_runtimes)) == ert);
- Vmodule_runtimes = XCDR (Vmodule_runtimes);
finalize_environment (ert->private_members->env);
}
DEFAULT_REHASH_SIZE, DEFAULT_REHASH_THRESHOLD,
Qnil, false);
- staticpro (&Vmodule_runtimes);
- Vmodule_runtimes = Qnil;
-
- staticpro (&Vmodule_environments);
- Vmodule_environments = Qnil;
-
DEFSYM (Qmodule_load_failed, "module-load-failed");
Fput (Qmodule_load_failed, Qerror_conditions,
pure_list (Qmodule_load_failed, Qerror));
case SPECPDL_UNWIND_EXCURSION:
case SPECPDL_UNWIND_VOID:
case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+ case SPECPDL_MODULE_RUNTIME:
+ case SPECPDL_MODULE_ENVIRONMENT:
+#endif
case SPECPDL_LET_LOCAL:
break;
case SPECPDL_UNWIND_EXCURSION:
case SPECPDL_UNWIND_VOID:
case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+ case SPECPDL_MODULE_RUNTIME:
+ case SPECPDL_MODULE_ENVIRONMENT:
+#endif
case SPECPDL_LET_LOCAL:
break;
grow_specpdl ();
}
+void
+record_unwind_protect_module (enum specbind_tag kind, void *ptr)
+{
+ specpdl_ptr->kind = kind;
+ specpdl_ptr->unwind_ptr.func = NULL;
+ specpdl_ptr->unwind_ptr.arg = ptr;
+ grow_specpdl ();
+}
+
void
rebind_for_thread_switch (void)
{
break;
case SPECPDL_BACKTRACE:
break;
+#ifdef HAVE_MODULES
+ case SPECPDL_MODULE_RUNTIME:
+ finalize_runtime_unwind (this_binding->unwind_ptr.arg);
+ break;
+ case SPECPDL_MODULE_ENVIRONMENT:
+ finalize_environment_unwind (this_binding->unwind_ptr.arg);
+ break;
+#endif
case SPECPDL_LET:
{ /* If variable has a trivial value (no forwarding), and isn't
trapped, we can just set it. */
case SPECPDL_UNWIND_INTMAX:
case SPECPDL_UNWIND_VOID:
case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+ case SPECPDL_MODULE_RUNTIME:
+ case SPECPDL_MODULE_ENVIRONMENT:
+#endif
break;
case SPECPDL_LET:
{ /* If variable has a trivial value (no forwarding), we can
case SPECPDL_UNWIND_EXCURSION:
case SPECPDL_UNWIND_VOID:
case SPECPDL_BACKTRACE:
+#ifdef HAVE_MODULES
+ case SPECPDL_MODULE_RUNTIME:
+ case SPECPDL_MODULE_ENVIRONMENT:
+#endif
break;
default:
}
break;
+#ifdef HAVE_MODULES
+ case SPECPDL_MODULE_RUNTIME:
+ break;
+ case SPECPDL_MODULE_ENVIRONMENT:
+ mark_module_environment (pdl->unwind_ptr.arg);
+ break;
+#endif
+
case SPECPDL_LET_DEFAULT:
case SPECPDL_LET_LOCAL:
mark_object (specpdl_where (pdl));
SPECPDL_UNWIND_EXCURSION, /* Likewise, on an excursion. */
SPECPDL_UNWIND_VOID, /* Likewise, with no arg. */
SPECPDL_BACKTRACE, /* An element of the backtrace. */
+#ifdef HAVE_MODULES
+ SPECPDL_MODULE_RUNTIME, /* A live module runtime. */
+ SPECPDL_MODULE_ENVIRONMENT, /* A live module environment. */
+#endif
SPECPDL_LET, /* A plain and simple dynamic let-binding. */
/* Tags greater than SPECPDL_LET must be "subkinds" of LET. */
SPECPDL_LET_LOCAL, /* A buffer-local let-binding. */
extern void record_unwind_protect_void (void (*) (void));
extern void record_unwind_protect_excursion (void);
extern void record_unwind_protect_nothing (void);
+extern void record_unwind_protect_module (enum specbind_tag, void *);
extern void clear_unwind_protect (ptrdiff_t);
extern void set_unwind_protect (ptrdiff_t, void (*) (Lisp_Object), Lisp_Object);
extern void set_unwind_protect_ptr (ptrdiff_t, void (*) (void *), void *);
(struct Lisp_Module_Function const *);
extern void *module_function_data (const struct Lisp_Module_Function *);
extern void module_finalize_function (const struct Lisp_Module_Function *);
-extern void mark_modules (void);
+extern void mark_module_environment (void *);
+extern void finalize_runtime_unwind (void *);
+extern void finalize_environment_unwind (void *);
extern void init_module_assertions (bool);
extern void syms_of_module (void);
#endif
return args[0];
}
+static emacs_value
+Fmod_test_funcall (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
+ void *data)
+{
+ assert (0 < nargs);
+ return env->funcall (env, args[0], nargs - 1, args + 1);
+}
+
/* Lisp utilities for easier readability (simple wrappers). */
/* Provide FEATURE to Emacs. */
DEFUN ("mod-test-function-finalizer-calls",
Fmod_test_function_finalizer_calls, 0, 0, NULL, NULL);
DEFUN ("mod-test-async-pipe", Fmod_test_async_pipe, 1, 1, NULL, NULL);
+ DEFUN ("mod-test-funcall", Fmod_test_funcall, 1, emacs_variadic_function,
+ NULL, NULL);
#undef DEFUN
(should (not (multibyte-string-p (mod-test-return-unibyte))))
(should (equal result "foo\x00zot"))))
+(cl-defstruct (emacs-module-tests--variable
+ (:constructor nil)
+ (:constructor emacs-module-tests--make-variable
+ (name
+ &aux
+ (mutex (make-mutex name))
+ (condvar (make-condition-variable mutex name))))
+ (:copier nil))
+ "A variable that's protected by a mutex."
+ value
+ (mutex nil :read-only t :type mutex)
+ (condvar nil :read-only t :type condition-variable))
+
+(defun emacs-module-tests--wait-for-variable (variable desired)
+ (with-mutex (emacs-module-tests--variable-mutex variable)
+ (while (not (eq (emacs-module-tests--variable-value variable) desired))
+ (condition-wait (emacs-module-tests--variable-condvar variable)))))
+
+(defun emacs-module-tests--change-variable (variable new)
+ (with-mutex (emacs-module-tests--variable-mutex variable)
+ (setf (emacs-module-tests--variable-value variable) new)
+ (condition-notify (emacs-module-tests--variable-condvar variable) :all)))
+
+(ert-deftest emacs-module-tests/interleaved-threads ()
+ (let* ((state-1 (emacs-module-tests--make-variable "1"))
+ (state-2 (emacs-module-tests--make-variable "2"))
+ (thread-1
+ (make-thread
+ (lambda ()
+ (emacs-module-tests--change-variable state-1 'before-module)
+ (mod-test-funcall
+ (lambda ()
+ (emacs-module-tests--change-variable state-1 'in-module)
+ (emacs-module-tests--wait-for-variable state-2 'in-module)))
+ (emacs-module-tests--change-variable state-1 'after-module))
+ "thread 1"))
+ (thread-2
+ (make-thread
+ (lambda ()
+ (emacs-module-tests--change-variable state-2 'before-module)
+ (emacs-module-tests--wait-for-variable state-1 'in-module)
+ (mod-test-funcall
+ (lambda ()
+ (emacs-module-tests--change-variable state-2 'in-module)
+ (emacs-module-tests--wait-for-variable state-1 'after-module)))
+ (emacs-module-tests--change-variable state-2 'after-module))
+ "thread 2")))
+ (thread-join thread-1)
+ (thread-join thread-2)))
+
;;; emacs-module-tests.el ends here