···188188 (David Allsopp, Antonin Décimo, and Samuel Hym, review by Nicolas
189189 Ojeda Bar)
190190191191-- #11911, #12383: Restore statmemprof functionality in part
192192- (backtrace buffers). (Nick Barnes, review by Gabriel Scherer)
193193-194194-- #11911, #12383: Restore statmemprof functionality in part (backtrace
195195- buffers). (Nick Barnes, review by Gabriel Scherer and Fabrice
196196- Buoro).
191191+- #11911, #12382, #12383: Restore statmemprof functionality in part
192192+ (backtrace buffers, per-thread and per-domain data
193193+ structures). (Nick Barnes, review by Gabriel Scherer, Fabrice Buoro,
194194+ Sadiq Jaffer, and Guillaume Munch-Maccagnoni).
197195198196- #12735: Store both ends of the stack chain in continuations
199197 (Leo White, review by Miod Vallat and KC Sivaramakrishnan)
+11-2
otherlibs/systhreads/st_stubs.c
···8888 value * gc_regs; /* saved value of Caml_state->gc_regs */
8989 value * gc_regs_buckets; /* saved value of Caml_state->gc_regs_buckets */
9090 void * exn_handler; /* saved value of Caml_state->exn_handler */
9191+ memprof_thread_t memprof; /* memprof's internal thread data structure */
91929293#ifndef NATIVE_CODE
9394 intnat trap_sp_off; /* saved value of Caml_state->trap_sp_off */
···225226 Caml_state->trap_barrier_off = th->trap_barrier_off;
226227 Caml_state->external_raise = th->external_raise;
227228#endif
229229+ caml_memprof_enter_thread(th->memprof);
228230}
229231230232CAMLprim value caml_thread_cleanup(value unit);
···297299 th->external_raise = NULL;
298300#endif
299301302302+ th->memprof = caml_memprof_new_thread(domain_state);
300303 return th;
301304}
302305···384387 th = Active_thread->next;
385388 while (th != Active_thread) {
386389 next = th->next;
390390+ caml_memprof_delete_thread(th->memprof);
387391 caml_thread_free_info(th);
388392 th = next;
389393 }
···459463 new_thread->next = new_thread;
460464 new_thread->prev = new_thread;
461465 new_thread->backtrace_last_exn = Val_unit;
466466+ new_thread->memprof = caml_memprof_main_thread(Caml_state);
462467463468 st_tls_set(caml_thread_key, new_thread);
464469465470 Active_thread = new_thread;
466466-471471+ caml_memprof_enter_thread(new_thread->memprof);
467472}
468473469474CAMLprim value caml_thread_yield(value unit);
···510515 caml_domain_external_interrupt_hook = caml_thread_interrupt_hook;
511516 caml_domain_initialize_hook = caml_thread_domain_initialize_hook;
512517 caml_domain_stop_hook = caml_thread_domain_stop_hook;
513513-514518 caml_atfork_hook = caml_thread_reinitialize;
515519516520 return Val_unit;
···543547 /* The main domain thread does not go through [caml_thread_stop]. There is
544548 always one more thread in the chain at this point in time. */
545549 CAMLassert(Active_thread->next != Active_thread);
550550+551551+ /* Tell memprof that this thread is terminating */
552552+ caml_memprof_delete_thread(Active_thread->memprof);
553553+546554 /* Signal that the thread has terminated */
547555 caml_threadstatus_terminate(Terminated(Active_thread->descr));
548556···674682 th->prev = Active_thread;
675683 Active_thread->next->prev = th;
676684 Active_thread->next = th;
685685+677686 /* Associate the thread descriptor with the thread */
678687 st_tls_set(caml_thread_key, (void *) th);
679688 /* Allocate the thread descriptor on the heap */
+10-6
runtime/caml/domain_state.tbl
···1515/**************************************************************************/
16161717DOMAIN_STATE(atomic_uintnat, young_limit)
1818+1819/* Minor heap limit. Typically [young_start] <= [young_limit] <=
1920 * [young_end], but this field can be set atomically to UINTNAT_MAX by
2020- * another thread (typically from another domain) in order to
2121- * interrupt this domain (by causing an allocation failure). Setting
2222- * [young_limit] to UINTNAT_MAX can be done safely at any time
2323- * whatsoever by any thread. To avoid races, setting [young_limit] to
2424- * anything else than UINTNAT_MAX should only be done via
2525- * [caml_reset_young_limit] by the domain itself. */
2121+ * another thread (typically from another domain), or by the memory
2222+ * profiler, in order to interrupt this domain (by causing an
2323+ * allocation failure). Setting [young_limit] to UINTNAT_MAX can be
2424+ * done safely at any time whatsoever by any thread. To avoid races,
2525+ * setting [young_limit] to anything else than UINTNAT_MAX should only
2626+ * be done via [caml_reset_young_limit] by the domain itself. */
26272728DOMAIN_STATE(value*, young_ptr)
2829/* Minor heap pointer */
···156157DOMAIN_STATE(int64_t, trap_barrier_block)
157158DOMAIN_STATE(struct caml_exception_context*, external_raise)
158159/* Bytecode TLS vars, not used for native code */
160160+161161+DOMAIN_STATE(struct memprof_domain_s *, memprof)
162162+DOMAIN_STATE(value *, memprof_young_trigger)
159163160164DOMAIN_STATE(extra_params_area, extra_params)
161165/* This member must occur last, because it is an array, not a scalar */
···5858#include "caml/intext.h"
5959#include "caml/major_gc.h"
6060#include "caml/minor_gc.h"
6161+#include "caml/memprof.h"
6162#include "caml/misc.h"
6263#include "caml/memory.h"
6364#include "caml/osdeps.h"
···518519 * major slice is scheduled. */
519520 domain_state->young_trigger = domain_state->young_start
520521 + (domain_state->young_end - domain_state->young_start) / 2;
522522+ caml_memprof_renew_minor_sample(domain_state);
521523 caml_reset_young_limit(domain_state);
522524523525 check_minor_heap();
···555557}
556558557559/* must be run on the domain's thread */
558558-static void domain_create(uintnat initial_minor_heap_wsize) {
560560+static void domain_create(uintnat initial_minor_heap_wsize,
561561+ caml_domain_state *parent)
562562+{
559563 dom_internal* d = 0;
560564 caml_domain_state* domain_state;
561565 struct interruptor* s;
···583587 CAMLassert(!s->running);
584588 CAMLassert(!s->interrupt_pending);
585589586586- domain_self = d;
587587-588590 /* If the chosen domain slot has not been previously used, allocate a fresh
589591 domain state. Otherwise, reuse it.
590592···606608 domain_state = d->state;
607609 }
608610611611+ s->unique_id = fresh_domain_unique_id();
612612+ s->running = 1;
613613+ atomic_fetch_add(&caml_num_domains_running, 1);
614614+615615+ /* Note: until we take d->domain_lock, the domain_state may still be
616616+ * shared with a domain which is terminating (see
617617+ * domain_terminate). */
618618+619619+ caml_plat_lock(&d->domain_lock);
620620+621621+ /* Set domain_self if we have successfully allocated the
622622+ * caml_domain_state. Otherwise domain_self will be NULL and it's up
623623+ * to the caller to deal with that. */
624624+625625+ domain_self = d;
609626 caml_state = domain_state;
610627611628 domain_state->young_limit = 0;
···615632 atomic_store_explicit(&s->interrupt_word, &domain_state->young_limit,
616633 memory_order_release);
617634618618- s->unique_id = fresh_domain_unique_id();
619619- s->running = 1;
620620- atomic_fetch_add(&caml_num_domains_running, 1);
621621-622622- caml_plat_lock(&d->domain_lock);
635635+ /* Tell memprof system about the new domain before either (a) new
636636+ * domain can allocate anything or (b) parent domain can go away. */
637637+ caml_memprof_new_domain(parent, domain_state);
638638+ if (!domain_state->memprof) {
639639+ goto init_memprof_failure;
640640+ }
623641624642 domain_state->id = d->id;
625643 domain_state->unique_id = d->interruptor.unique_id;
···730748 caml_free_minor_tables(domain_state->minor_tables);
731749 domain_state->minor_tables = NULL;
732750alloc_minor_tables_failure:
751751+ caml_memprof_delete_domain(domain_state);
752752+init_memprof_failure:
733753 domain_self = NULL;
734754735755···921941 dom->backup_thread_msg = BT_INIT;
922942 }
923943924924- domain_create(minor_heap_wsz);
944944+ domain_create(minor_heap_wsz, NULL);
925945 if (!domain_self) caml_fatal_error("Failed to create main domain");
926946 CAMLassert (domain_self->state->unique_id == 0);
927947···968988 parameters returned to the parent by the child.
969989*/
970990struct domain_startup_params {
971971- struct interruptor* parent; /* in */
991991+ dom_internal *parent; /* in */
972992 enum domain_status status; /* in+out:
973993 parent and child synchronize on this value. */
974994 struct domain_ml_values* ml_values; /* in */
···11561176 }
11571177#endif
1158117811591159- domain_create(caml_params->init_minor_heap_wsz);
11791179+ domain_create(caml_params->init_minor_heap_wsz, p->parent->state);
11801180+11811181+ if (!domain_self) {
11821182+ caml_fatal_error("Failed to create domain");
11831183+ }
11841184+11601185 /* this domain is now part of the STW participant set */
11611186 p->newdom = domain_self;
1162118711631188 /* handshake with the parent domain */
11641164- caml_plat_lock(&p->parent->lock);
11891189+ caml_plat_lock(&p->parent->interruptor.lock);
11651190 if (domain_self) {
11661191 p->status = Dom_started;
11671192 p->unique_id = domain_self->interruptor.unique_id;
11681193 } else {
11691194 p->status = Dom_failed;
11701195 }
11711171- caml_plat_broadcast(&p->parent->cond);
11721172- caml_plat_unlock(&p->parent->lock);
11961196+ caml_plat_broadcast(&p->parent->interruptor.cond);
11971197+ caml_plat_unlock(&p->parent->interruptor.lock);
11731198 /* Cannot access p below here. */
1174119911751200 if (domain_self) {
···12281253 if (caml_debugger_in_use)
12291254 caml_fatal_error("ocamldebug does not support spawning multiple domains");
12301255#endif
12311231- p.parent = &domain_self->interruptor;
12561256+ p.parent = domain_self;
12321257 p.status = Dom_starting;
1233125812341259 p.ml_values =
···16201645 races. */
16211646void caml_reset_young_limit(caml_domain_state * dom_st)
16221647{
16231623- CAMLassert ((uintnat)dom_st->young_ptr >= (uintnat)dom_st->young_trigger);
16241648 /* An interrupt might have been queued in the meanwhile; the
16251649 atomic_exchange achieves the proper synchronisation with the
16261650 reads that follow (an atomic_store is not enough). */
16271627- atomic_exchange(&dom_st->young_limit, (uintnat)dom_st->young_trigger);
16281628- /* In case of actions that we never delay, interrupt the domain
16291629- again immediately. */
16511651+ value *trigger = dom_st->young_trigger > dom_st->memprof_young_trigger ?
16521652+ dom_st->young_trigger : dom_st->memprof_young_trigger;
16531653+ CAMLassert ((uintnat)dom_st->young_ptr >
16541654+ (uintnat)dom_st->memprof_young_trigger);
16551655+ CAMLassert ((uintnat)dom_st->young_ptr >
16561656+ (uintnat)dom_st->young_trigger);
16571657+ /* An interrupt might have been queued in the meanwhile; this
16581658+ achieves the proper synchronisation. */
16591659+ atomic_exchange(&dom_st->young_limit, (uintnat)trigger);
16601660+16301661 dom_internal * d = &all_domains[dom_st->id];
16311662 if (atomic_load_relaxed(&d->interruptor.interrupt_pending)
16321663 || dom_st->requested_minor_gc
···18971928 }
18981929 caml_plat_unlock(&all_domains_lock);
18991930 }
19311931+19321932+ /* domain_state may be re-used by a fresh domain here (now that we
19331933+ * have done remove_from_stw_domains and released the
19341934+ * all_domains_lock). However, domain_create() won't touch it until
19351935+ * it has claimed the domain_lock, so we hang onto that while we are
19361936+ * tearing down the state. */
19371937+19381938+ /* Delete the domain state from statmemprof after any promotion
19391939+ * (etc) done by this domain: any remaining memprof state will be
19401940+ * handed over to surviving domains. */
19411941+ caml_memprof_delete_domain(domain_state);
19421942+19001943 /* We can not touch domain_self->interruptor after here
19011944 because it may be reused */
19021945 caml_remove_generational_global_root(&domain_state->dls_root);
+257-83
runtime/memprof.c
···15151616#define CAML_INTERNALS
17171818+#include <stdbool.h>
1919+#include "caml/memory.h"
2020+#include "caml/memprof.h"
2121+2222+/* type aliases for the hierarchy of structures for managing memprof status. */
2323+2424+typedef struct memprof_domain_s memprof_domain_s, *memprof_domain_t;
2525+typedef struct memprof_thread_s memprof_thread_s, *memprof_thread_t;
2626+2727+/* Per-thread memprof state. */
2828+2929+struct memprof_thread_s {
3030+ /* [suspended] is used for inhibiting memprof callbacks when
3131+ a callback is running or when an uncaught exception handler is
3232+ called. */
3333+ bool suspended;
3434+3535+ /* TODO: More fields to add here */
3636+3737+ /* Per-domain memprof information */
3838+ memprof_domain_t domain;
3939+4040+ /* Linked list of thread structures for this domain. Could use a
4141+ * doubly-linked list for performance, but I haven't measured it. */
4242+ memprof_thread_t next;
4343+};
4444+4545+/* A memprof configuration is held in an object on the Caml
4646+ * heap. These are getter macros for each field. */
4747+4848+#define Stopped(config) Bool_val(Field(config, 0))
4949+#define Running(config) ((config != Val_unit) && !Stopped(config))
5050+#define Lambda(config) Double_val(Field(config, 1))
5151+#define One_log1m_lambda(config) Double_val(Field(config, 2))
5252+#define Callstack_size(config) Int_val(Field(config, 3)
5353+#define Alloc_minor(config) Field(config, 4)
5454+#define Alloc_major(config) Field(config, 5)
5555+#define Promote(config) Field(config, 6)
5656+#define Dealloc_minor(config) Field(config, 7)
5757+#define Dealloc_major(config) Field(config, 8)
5858+5959+/* The 'stopped' field is the only one we ever update. */
6060+6161+#define Set_stopped(config, flag) (Field(config, 0) = Val_bool(flag))
6262+6363+/* Per-domain memprof state */
6464+6565+struct memprof_domain_s {
6666+ /* The owning domain */
6767+ caml_domain_state *caml_state;
6868+6969+ /* Linked list of threads in this domain */
7070+ memprof_thread_t threads;
7171+7272+ /* The current thread's memprof state. Note that there may not be a
7373+ "current thread". TODO: maybe this shouldn't be nullable.
7474+ Nullability costs us some effort and may be meaningless. See call
7575+ site of caml_memprof_leave_thread() in st_stubs.c. */
7676+ memprof_thread_t current;
7777+7878+ /* TODO: More fields to add here */
7979+8080+ /* The current profiling configuration for this domain. */
8181+ value config;
8282+};
8383+8484+/**** Create and destroy thread state structures ****/
8585+8686+static memprof_thread_t thread_create(memprof_domain_t domain)
8787+{
8888+ memprof_thread_t thread = caml_stat_alloc(sizeof(memprof_thread_s));
8989+ if (!thread) {
9090+ return NULL;
9191+ }
9292+ thread->suspended = false;
9393+9494+ /* attach to domain record */
9595+ thread->domain = domain;
9696+ thread->next = domain->threads;
9797+ domain->threads = thread;
9898+9999+ return thread;
100100+}
101101+102102+static void thread_destroy(memprof_thread_t thread)
103103+{
104104+ memprof_domain_t domain = thread->domain;
105105+106106+ if (domain->current == thread) {
107107+ domain->current = NULL;
108108+ }
109109+ /* remove thread from the per-domain list. Could go faster if we
110110+ * used a doubly-linked list, but that's premature optimisation
111111+ * at this point. */
112112+ memprof_thread_t *p = &domain->threads;
113113+ while (*p != thread) {
114114+ p = &(*p)->next;
115115+ }
116116+117117+ *p = thread->next;
118118+119119+ caml_stat_free(thread);
120120+}
121121+122122+/**** Create and destroy domain state structures ****/
123123+124124+static void domain_destroy(memprof_domain_t domain)
125125+{
126126+ memprof_thread_t thread = domain->threads;
127127+ while (thread) {
128128+ memprof_thread_t next = thread->next;
129129+ thread_destroy(thread);
130130+ thread = next;
131131+ }
132132+133133+ caml_stat_free(domain);
134134+}
135135+136136+static memprof_domain_t domain_create(caml_domain_state *caml_state)
137137+{
138138+ memprof_domain_t domain = caml_stat_alloc(sizeof(memprof_domain_s));
139139+ if (!domain) {
140140+ return NULL;
141141+ }
142142+143143+ domain->caml_state = caml_state;
144144+ domain->threads = NULL;
145145+ domain->current = NULL;
146146+ domain->config = Val_unit;
147147+148148+ /* create initial thread for domain */
149149+ memprof_thread_t thread = thread_create(domain);
150150+ if (thread) {
151151+ domain->current = thread;
152152+ } else {
153153+ domain_destroy(domain);
154154+ domain = NULL;
155155+ }
156156+ return domain;
157157+}
158158+159159+/**** Interface to domain module ***/
160160+161161+void caml_memprof_new_domain(caml_domain_state *parent,
162162+ caml_domain_state *child)
163163+{
164164+ memprof_domain_t domain = domain_create(child);
165165+166166+ child->memprof = domain;
167167+ /* domain inherits configuration from parent */
168168+ if (domain && parent) {
169169+ domain->config = parent->memprof->config;
170170+ }
171171+}
172172+173173+void caml_memprof_delete_domain(caml_domain_state *state)
174174+{
175175+ if (!state->memprof) {
176176+ return;
177177+ }
178178+ domain_destroy(state->memprof);
179179+ state->memprof = NULL;
180180+}
181181+182182+/**** Interface with domain action-pending flag ****/
183183+184184+/* If profiling is active in the current domain, and we may have some
185185+ * callbacks pending, set the action pending flag. */
186186+187187+static void set_action_pending_as_needed(memprof_domain_t domain)
188188+{
189189+ /* if (condition) caml_set_action_pending(domain->caml_state); */
190190+}
191191+192192+/* Set the suspended flag on `domain` to `s`. */
193193+194194+static void update_suspended(memprof_domain_t domain, bool s)
195195+{
196196+ if (domain->current) {
197197+ domain->current->suspended = s;
198198+ }
199199+ caml_memprof_renew_minor_sample(domain->caml_state);
200200+ if (!s) set_action_pending_as_needed(domain);
201201+}
202202+203203+/* Set the suspended flag on the current domain to `s`. */
204204+205205+void caml_memprof_update_suspended(bool s) {
206206+ update_suspended(Caml_state->memprof, s);
207207+}
208208+209209+/**** Sampling procedures ****/
210210+211211+Caml_inline bool running(memprof_domain_t domain)
212212+{
213213+ memprof_thread_t thread = domain->current;
214214+215215+ if (thread && !thread->suspended) {
216216+ value config = domain->config;
217217+ return Running(config);
218218+ }
219219+ return false;
220220+}
221221+222222+/* Renew the next sample in a domain's minor heap. Could race with
223223+ * sampling and profile-stopping code, so do not call from another
224224+ * domain unless the world is stopped. Must be called after each minor
225225+ * sample and after each minor collection. In practice, this is called
226226+ * at each minor sample, at each minor collection, and when sampling
227227+ * is suspended and unsuspended. Extra calls do not change the
228228+ * statistical properties of the sampling because of the
229229+ * memorylessness of the geometric distribution. */
230230+231231+void caml_memprof_renew_minor_sample(caml_domain_state *state)
232232+{
233233+ memprof_domain_t domain = state->memprof;
234234+ value *trigger = state->young_start;
235235+ if (running(domain)) {
236236+ /* set trigger based on geometric distribution */
237237+ }
238238+ CAMLassert((trigger >= state->young_start) &&
239239+ (trigger < state->young_ptr));
240240+ state->memprof_young_trigger = trigger;
241241+ caml_reset_young_limit(state);
242242+}
243243+244244+/**** Interface with systhread. ****/
245245+246246+CAMLexport memprof_thread_t caml_memprof_new_thread(caml_domain_state *state)
247247+{
248248+ return thread_create(state->memprof);
249249+}
250250+251251+CAMLexport memprof_thread_t caml_memprof_main_thread(caml_domain_state *state)
252252+{
253253+ memprof_domain_t domain = state->memprof;
254254+ memprof_thread_t thread = domain->threads;
255255+256256+ /* There should currently be just one thread in this domain */
257257+ CAMLassert(thread);
258258+ CAMLassert(thread->next == NULL);
259259+ return thread;
260260+}
261261+262262+CAMLexport void caml_memprof_delete_thread(memprof_thread_t thread)
263263+{
264264+ thread_destroy(thread);
265265+}
266266+267267+CAMLexport void caml_memprof_enter_thread(memprof_thread_t thread)
268268+{
269269+ thread->domain->current = thread;
270270+ update_suspended(thread->domain, thread->suspended);
271271+}
272272+273273+/**** Interface to OCaml ****/
274274+18275#include "caml/fail.h"
1927620277CAMLprim value caml_memprof_start(value lv, value szv, value tracker_param)
···575832 caml_set_action_pending(Caml_state);
576833}
577834578578-void caml_memprof_set_suspended(int s)
579579-{
580580- local->suspended = s;
581581- caml_memprof_renew_minor_sample();
582582- if (!s) check_action_pending();
583583-}
584584-585835/* In case of a thread context switch during a callback, this can be
586836 called in a reetrant way. */
587837value caml_memprof_handle_postponed_exn(void)
···8121062 caml_reset_young_limit(Caml_state);
8131063}
8141064815815-/* Renew the next sample in the minor heap. This needs to be called
816816- after each minor sampling and after each minor collection. In
817817- practice, this is called at each sampling in the minor heap and at
818818- each minor collection. Extra calls do not change the statistical
819819- properties of the sampling because of the memorylessness of the
820820- geometric distribution. */
821821-void caml_memprof_renew_minor_sample(void)
822822-{
823823- if (lambda == 0 || local->suspended)
824824- /* No trigger in the current minor heap. */
825825- caml_memprof_young_trigger = Caml_state->young_alloc_start;
826826- else {
827827- uintnat geom = rand_geom();
828828- if (Caml_state->young_ptr - Caml_state->young_alloc_start < geom)
829829- /* No trigger in the current minor heap. */
830830- caml_memprof_young_trigger = Caml_state->young_alloc_start;
831831- else
832832- caml_memprof_young_trigger = Caml_state->young_ptr - (geom - 1);
833833- }
834834-835835- caml_reset_young_limit(Caml_state);
836836-}
837837-8381065/* Called when exceeding the threshold for the next sample in the
8391066 minor heap, from the C code (the handling is different when called
8401067 from natively compiled OCaml code). */
···10731300 ea->t = NULL;
10741301 }
10751302}
10761076-10771077-static void th_ctx_memprof_stop(struct caml_memprof_th_ctx* ctx, void* data)
10781078-{
10791079- (void)data;
10801080- if (ctx->callback_status != CB_IDLE) ctx->callback_status = CB_STOPPED;
10811081- empty_entry_array(&ctx->entries);
10821082-}
10831083-10841303CAMLprim value caml_memprof_stop(value unit)
10851304{
10861305 if (!started) caml_failwith("Gc.Memprof.stop: not started.");
···11071326 callstack_buffer_len = 0;
1108132711091328 return Val_unit;
11101110-}
11111111-11121112-/**** Interface with systhread. ****/
11131113-11141114-static void th_ctx_iter_default(th_ctx_action f, void* data) {
11151115- f(local, data);
11161116-}
11171117-11181118-CAMLexport void (*caml_memprof_th_ctx_iter_hook)(th_ctx_action, void*)
11191119- = th_ctx_iter_default;
11201120-11211121-CAMLexport struct caml_memprof_th_ctx* caml_memprof_new_th_ctx()
11221122-{
11231123- struct caml_memprof_th_ctx* ctx =
11241124- caml_stat_alloc(sizeof(struct caml_memprof_th_ctx));
11251125- ctx->suspended = 0;
11261126- ctx->callback_status = CB_IDLE;
11271127- ctx->entries.t = NULL;
11281128- ctx->entries.min_alloc_len = MIN_ENTRIES_LOCAL_ALLOC_LEN;
11291129- ctx->entries.alloc_len = ctx->entries.len = 0;
11301130- ctx->entries.young_idx = ctx->entries.delete_idx = 0;
11311131- return ctx;
11321132-}
11331133-11341134-CAMLexport void caml_memprof_delete_th_ctx(struct caml_memprof_th_ctx* ctx)
11351135-{
11361136- if (ctx->callback_status >= 0)
11371137- /* A callback is running in this thread from the global entries
11381138- array. We delete the corresponding entry. */
11391139- mark_deleted(&entries_global, ctx->callback_status);
11401140- if (ctx == local) local = NULL;
11411141- caml_stat_free(ctx->entries.t);
11421142- if (ctx != &caml_memprof_main_ctx) caml_stat_free(ctx);
11431143-}
11441144-11451145-CAMLexport void caml_memprof_leave_thread(void)
11461146-{
11471147- local = NULL;
11481148-}
11491149-11501150-CAMLexport void caml_memprof_enter_thread(struct caml_memprof_th_ctx* ctx)
11511151-{
11521152- CAMLassert(local == NULL);
11531153- local = ctx;
11541154- caml_memprof_set_suspended(ctx->suspended);
11551329}
1156133011571331#endif
+7
runtime/printexc.c
···17171818/* Print an uncaught exception and abort */
19192020+#include <stdbool.h>
2021#include <stdio.h>
2122#include <stdlib.h>
2223#include <string.h>
···142143143144 handle_uncaught_exception =
144145 caml_named_value("Printexc.handle_uncaught_exception");
146146+147147+ /* If the callback allocates, memprof could be called, in which case
148148+ a memprof callback could raise an exception while
149149+ [handle_uncaught_exception] is running, and the printing of
150150+ the exception could fail. */
151151+ caml_memprof_update_suspended(true);
145152146153 if (handle_uncaught_exception != NULL)
147154 /* [Printexc.handle_uncaught_exception] does not raise exception. */