-
Notifications
You must be signed in to change notification settings - Fork 149
Expand file tree
/
Copy pathcontext.h
More file actions
610 lines (535 loc) · 17.4 KB
/
context.h
File metadata and controls
610 lines (535 loc) · 17.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
/*
* This file is part of AtomVM.
*
* Copyright 2017 Davide Bettio <davide@uninstall.it>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
*/
/**
* @file context.h
* @brief Context struct and related management functions
*
* @details A context represent the state of a running erlang process or port.
*/
#ifndef _CONTEXT_H_
#define _CONTEXT_H_
#include "globalcontext.h"
#include "list.h"
#include "mailbox.h"
#include "smp.h"
#include "term.h"
#include "timer_list.h"
#ifdef __cplusplus
extern "C" {
#endif
struct Module;
#ifndef TYPEDEF_MODULE
#define TYPEDEF_MODULE
typedef struct Module Module;
#endif
typedef enum NativeHandlerResult
{
NativeTerminate = 1,
NativeContinue
} NativeHandlerResult;
// Native handlers. Return NativeTerminate to terminate, NativeContinue
// to keep the handler in the process table.
typedef NativeHandlerResult (*native_handler_f)(Context *ctx);
enum ContextFlags
{
NoFlags = 0,
WaitingTimeout = 1,
WaitingTimeoutExpired = 2,
Running = 4,
Ready = 8,
Killed = 16,
Trap = 32,
Distribution = 64,
};
enum HeapGrowthStrategy
{
BoundedFreeHeapGrowth = 0,
MinimumHeapGrowth,
FibonacciHeapGrowth
};
// Max number of x(N) & fr(N) registers
// BEAM sets this to 1024.
// x regs have an additional register that is used for storing an additional working element
// so the number of registers is MAX_REG + 1, but only MAX_REG are addressable as x registers
#define MAX_REG 16
struct Context
{
// JIT compiler expects fields to be at given offset or sizes.
// Do not change order or size without bumping the jit version
// First fields matches ErlNifEnv structure.
GlobalContext *global;
Heap heap;
term *e;
// Following fields offsets are also hard-coded in jit backends
term x[MAX_REG + 1];
term cp;
avm_float_t *fr;
term bs;
size_t bs_offset;
// End of hard-coded section
struct ListHead extended_x_regs;
struct ListHead processes_list_head;
struct ListHead processes_table_head;
int32_t process_id;
struct TimerListItem timer_list_head;
struct ListHead monitors_head;
size_t min_heap_size;
size_t max_heap_size;
enum HeapGrowthStrategy heap_growth_strategy;
// saved state when scheduled out
Module *saved_module;
const void *saved_ip;
// pointer to a trap or wait timeout handler
void *restore_trap_handler;
Mailbox mailbox;
struct ListHead dictionary;
// Ports support
native_handler_f native_handler;
int reductions;
unsigned int leader : 1;
unsigned int has_min_heap_size : 1;
unsigned int has_max_heap_size : 1;
bool trap_exit : 1;
#ifdef ENABLE_ADVANCED_TRACE
unsigned int trace_calls : 1;
unsigned int trace_call_args : 1;
unsigned int trace_returns : 1;
unsigned int trace_send : 1;
unsigned int trace_receive : 1;
#endif
enum ContextFlags ATOMIC flags;
void *platform_data;
term group_leader;
term exit_reason;
};
#ifndef TYPEDEF_CONTEXT
#define TYPEDEF_CONTEXT
typedef struct Context Context;
#endif
enum ContextMonitorType
{
CONTEXT_MONITOR_LINK_LOCAL,
CONTEXT_MONITOR_MONITORING_LOCAL,
CONTEXT_MONITOR_MONITORED_LOCAL,
CONTEXT_MONITOR_RESOURCE,
CONTEXT_MONITOR_LINK_REMOTE,
CONTEXT_MONITOR_MONITORING_LOCAL_REGISTEREDNAME,
};
#define UNLINK_ID_LINK_ACTIVE 0x0
/**
* @brief A regular monitor or a half link.
*/
struct Monitor
{
struct ListHead monitor_list_head;
enum ContextMonitorType monitor_type;
};
struct LinkLocalMonitor
{
struct Monitor monitor;
uint64_t unlink_id;
term link_local_process_id;
};
struct MonitorLocalMonitor
{
struct Monitor monitor;
uint64_t ref_ticks;
term monitor_obj;
};
struct MonitorLocalRegisteredNameMonitor
{
struct Monitor monitor;
uint64_t ref_ticks;
int32_t monitor_process_id;
term monitor_name;
};
// The other half is called ResourceMonitor and is a linked list of resources
struct ResourceContextMonitor
{
struct Monitor monitor;
uint64_t ref_ticks;
void *resource_obj;
};
struct LinkRemoteMonitor
{
struct Monitor monitor;
uint64_t unlink_id;
term node;
uint32_t pid_number;
uint32_t pid_serial;
uint32_t creation;
};
struct ExtendedRegister
{
struct ListHead head;
unsigned int index;
term value;
};
/**
* @brief Creates a new context
*
* @details Allocates a new Context struct and initialize it. The newly created
* context is also inserted into the processes table, however it is not scheduled,
* allowing for further initialization.
* @param glb The global context of this virtual machine instance.
* @returns created context.
*/
Context *context_new(GlobalContext *glb);
/**
* @brief Destroys a context
*
* @details Frees context resources and memory and removes it from the processes table.
* This should be called from the scheduler only. To actually delete a context that
* was created with context_new, use scheduler_terminate.
* @param c the context that will be destroyed.
*/
void context_destroy(Context *c);
/**
* @brief Ensure we have FP registers, allocating them if necessary.
* @param c context fo allocate FP registers for
*/
static inline void context_ensure_fpregs(Context *c)
{
if (UNLIKELY(c->fr == NULL)) {
c->fr = (avm_float_t *) malloc(sizeof(avm_float_t) * MAX_REG);
if (UNLIKELY(c->fr == NULL)) {
fprintf(stderr, "Could not allocate FP registers\n");
AVM_ABORT();
}
}
}
/**
* @brief Starts executing a function
*
* @details Start executing bytecode for the specified function, this function will block until it terminates. The outcome is saved to x[0] register.
* @param ctx the context that will be used to run the specified functions, x registers must be set to function arguments.
* @param mod the module name C string.
* @param function_name the function name C string.
* @param arity the function arity (number of arguments that are required).
* @returns 1 if an error occurred, otherwise 0 is always returned.
*/
int context_execute_loop(Context *ctx, Module *mod, const char *function_name, int arity);
/**
* @brief Returns 1 if the context is a port driver
*
* @details Checks if the given context has a native_handler or not.
* @param ctx a valid context
* @returns 1 if ctx is a port driver, otherwise 0 is returned.
*/
static inline int context_is_port_driver(const Context *ctx)
{
return ctx->native_handler != NULL;
}
/**
* @brief Cleans up unused registers
*
* @details Sets to NIL unused registers, x[0] - x[live - 1] will not be overwritten.
* @param ctx a valid context
* @param live number of used registers
*/
static inline void context_clean_registers(Context *ctx, int live)
{
for (int i = live; i < MAX_REG; i++) {
ctx->x[i] = term_nil();
}
}
/**
* @brief Returns a context's stack base
*
* @details Used for stack traces
* @param ctx a valid context.
* @returns the stack base
*/
static inline term *context_stack_base(const Context *ctx)
{
// Find which fragment the stack belongs to.
if (ctx->e >= ctx->heap.heap_start && ctx->e <= ctx->heap.heap_end) {
return ctx->heap.heap_end;
}
HeapFragment *fragment = ctx->heap.root->next;
while (fragment) {
if (ctx->e >= fragment->storage && ctx->e <= fragment->heap_end) {
return fragment->heap_end;
}
fragment = fragment->next;
}
fprintf(stderr, "Context heap is corrupted, cannot find fragment with stack pointer.\n");
AVM_ABORT();
}
/**
* @brief Returns a context's stack size
*
* @details Return the number of terms currently on the stack. Used for
* stack traces.
* @param ctx a valid context.
* @returns stack size in terms
*/
static inline size_t context_stack_size(const Context *ctx)
{
term *stack_base = context_stack_base(ctx);
return stack_base - ctx->e;
}
/**
* @brief Returns available free memory in term units
*
* @details Returns the number of terms that can fit either on the heap.
* @param ctx a valid context.
* @returns available free memory that is avail_size_in_bytes / sizeof(term).
*/
static inline size_t context_avail_free_memory(const Context *ctx)
{
// Check if stack is on current fragment
if (ctx->e <= ctx->heap.heap_end && ctx->e >= ctx->heap.heap_start) {
return ctx->e - ctx->heap.heap_ptr;
}
return ctx->heap.heap_end - ctx->heap.heap_ptr;
}
/**
* @brief Compares a term with an AtomString.
*
* @details Checks if the given term and the given AtomString refers to the same atom.
* This function is just a shortcut that uses the corresponding funtion from globalcontext.
* @param ctx the current Context.
* @param atom_a any term of any type, when it is not an atom false is always returned.
* @param atom_string_b an atom string, which is the atom length followed by atom characters.
* @returns true if they both refer to the same atom, otherwise false.
*/
static inline bool context_is_term_equal_to_atom_string(Context *ctx, term atom_a, AtomString atom_string_b)
{
return globalcontext_is_term_equal_to_atom_string(ctx->global, atom_a, atom_string_b);
}
/**
* @brief Returns number of messages in the process's mailbox
*
* @param ctx a valid context.
* @returns the number of messages in the process's mailbox
*/
size_t context_message_queue_len(Context *ctx);
/**
* @brief Returns total amount of size (in byes) occupied by the process.
*
* @param ctx a valid context.
* @returns total amount of size (in byes) occupied by the process
*/
size_t context_size(Context *ctx);
/**
* @brief Set or clear a flag on another context.
*
* @details atomically update flags <- (flags & mask) | value
* @param ctx the context to set/clear flag on.
* @param mask the mask to apply on flags
* @param value the value to set
*/
void context_update_flags(Context *ctx, int mask, int value);
/**
* @brief Get flags on a given context.
*
* @param ctx the context to get flags on.
* @param mask the mask to apply on flags
*/
static inline int context_get_flags(Context *ctx, int mask)
{
return ctx->flags & mask;
}
/**
* @brief Process a kill signal, setting the exit reason and changing the
* killed flag.
*
* @param ctx the context being executed
* @param signal the kill message
*/
void context_process_kill_signal(Context *ctx, struct TermSignal *signal);
/**
* @brief Process a process info request signal.
*
* @param ctx the context being executed
* @param signal the process info signal
* @param process_table_locked whether process table is already locked
*/
void context_process_process_info_request_signal(Context *ctx, struct BuiltInAtomRequestSignal *signal, bool process_table_locked);
/**
* @brief Process a trap answer signal.
*
* @param ctx the context being executed
* @param signal the answer message
* @return \c true if successful, \c false in case of memory error
*/
bool context_process_signal_trap_answer(Context *ctx, struct TermSignal *signal);
/**
* @brief Process a flush monitor signal.
*
* @param ctx the context being executed
* @param ref_ticks the monitor reference
* @param info whether to return FALSE_ATOM if no message was flushed.
*/
void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool info);
/**
* @brief Process set group leader signal
*
* @param ctx the context being executed
* @param signal the message with the group leader term
* @return \c true if successful, \c false in case of memory error
*/
bool context_process_signal_set_group_leader(Context *ctx, const struct TermSignal *signal);
/**
* @brief Process a link exit signal.
*
* @param ctx the context being executed
* @param signal the signal with the exit info tuple
* @return true if the process is trapping exit and info tuple was enqueued as a message;
*/
bool context_process_link_exit_signal(Context *ctx, struct TermSignal *signal);
/**
* @brief Process a monitor down signal.
*
* @param ctx the context being executed
* @param signal the signal with the down info tuple
*/
void context_process_monitor_down_signal(Context *ctx, struct TermSignal *signal);
/**
* @brief Get process information.
*
* @param ctx the context being executed
* @param out the answer term. Can be NULL if only the size matters.
* @param term_size the size of the answer term, in words.
* @param atom_key the key representing the info to get
* @param heap the heap to allocate the answer to
* @return \c true if successful, \c false in case of an error in which case
* *out is filled with an exception atom if it was not NULL
*/
bool context_get_process_info(Context *ctx, term *out, size_t *term_size, term atom_key, Heap *heap);
/**
* @brief Half-link process to another process
*
* @param link_pid process to link to (local or remote)
* @return the allocated monitor or NULL if allocation failed
*/
struct Monitor *monitor_link_new(term link_pid);
/**
* @brief Create a monitor on a process.
*
* @param monitor_pid monitored process
* @param ref_ticks reference of the monitor
* @param is_monitoring if ctx is the monitoring process
* @return the allocated monitor or NULL if allocation failed
*/
struct Monitor *monitor_new(term monitor_pid, uint64_t ref_ticks, bool is_monitoring);
/**
* @brief Create a monitor on a process by registered name.
*
* @param monitor_process_id monitored process id
* @param monitor_name name of the monitor (atom)
* @param ref_ticks reference of the monitor
* @param is_monitoring if ctx is the monitoring process
* @return the allocated monitor or NULL if allocation failed
*/
struct Monitor *monitor_registeredname_monitor_new(int32_t monitor_process_id, term monitor_name, uint64_t ref_ticks);
/**
* @brief Create a resource monitor.
*
* @param resource resource object
* @param ref_ticks reference associated with the monitor
* @return the allocated resource monitor or NULL if allocation failed
*/
struct Monitor *monitor_resource_monitor_new(void *resource, uint64_t ref_ticks);
/**
* @brief Half-unlink process to another process
* @details If process is found, an unlink id is generated and the link is
* deactivated.
*
* @param ctx the context being executed
* @param link_pid process to unlink from
* @param unlink_id on output, unlink id to send to the target process
* @return true if process was found
*/
bool context_set_unlink_id(Context *ctx, term link_pid, uint64_t *unlink_id);
/**
* @brief Half-unlink process to another process
* @details Called within the process only when an UnlinkID signal is received.
* If link is found, remove it and sends an UnlinkIDAck signal to the linked
* process.
*
* @param ctx the context being executed
* @param link_pid process to unlink from
* @param unlink_id unlink id from the signal
* @param process_table_locked whether process table is already locked
*/
void context_ack_unlink(Context *ctx, term link_pid, uint64_t unlink_id, bool process_table_locked);
/**
* @brief Half-unlink process to another process
* @details Called within the process only when an UnlinkIDAck signal is received.
* If link is found and matches, remove it.
*
* @param ctx the context being executed
* @param link_pid process to unlink from
* @param unlink_id unlink id from the signal
*/
void context_unlink_ack(Context *ctx, term link_pid, uint64_t unlink_id);
/**
* @brief Destroy a monitor on a process (monitoring, monitored or resource)
* @details Called within the process only. This function is called from
* DemonitorSignal as well as demonitor nif on monitoring process.
*
* @param ctx the context being executed (monitoring or monitored)
* @param ref_ticks reference of the monitor to remove
*/
void context_demonitor(Context *ctx, uint64_t ref_ticks);
/**
* @brief Get target of a monitor.
*
* @param ctx the context being executed
* @param ref_ticks reference of the monitor to remove
* @param is_monitoring whether ctx is the monitoring process.
* @return pid of monitored or monitoring process or term_invalid()
* if no monitor could be found.
*/
term context_get_monitor_pid(Context *ctx, uint64_t ref_ticks, bool *is_monitoring);
/**
* @brief Add a monitor on a process.
* @details Called within the process only. This function is called from
* MonitorSignal. Monitor is not added if it already exists. Monitors are
* identified by a reference, but links have no reference and a link can
* only exist once.
*
* @param ctx the context being executed
* @param new_monitor monitor object to add (ownership belongs to context
* afterwards)
* @return true if the monitor was added, false if it already existed and
* new_monitor waw freed.
*/
bool context_add_monitor(Context *ctx, struct Monitor *new_monitor);
/**
* @brief Get catch label from stack
*
* @param ctx the context being executed
* @param mod on output, the module
* @return the found label or 0 if no catch label was found in the stack
*/
int context_get_catch_label(Context *ctx, Module **mod);
/**
* @brief Dump context to stderr
*
* @param ctx context to dump
*/
void context_dump(Context *ctx);
#ifdef __cplusplus
}
#endif
#endif