summaryrefslogtreecommitdiff
path: root/erts/emulator/beam/code_ix.h
blob: 54ceefcc9b973dc2f20475746c42f5576a53516f (plain)
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
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2012-2023. All Rights Reserved.
 *
 * 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.
 *
 * %CopyrightEnd%
 */

/* Description:
 *	This is the interface that facilitates changing the beam code
 *      (load,upgrade,delete) while allowing executing Erlang processes to
 *      access the code without any locks or other expensive memory barriers.
 *
 *      The basic idea is to maintain several "logical copies" of the code. These
 *      copies are identified by a global 'code index', an integer of 0, 1 or 2.
 *      The code index is used as argument to code access structures like
 *      export, module, beam_catches, beam_ranges.
 *
 *      The current 'active' code index is used to access the current running
 *      code. The 'staging' code index is used by the process that performs
 *      a code change operation. When a code change operation completes
 *      successfully, the staging code index becomes the new active code index.
 *
 *      The third code index is not explicitly used. It can be thought of as
 *      the "previous active" or the "next staging" index. It is needed to make
 *      sure that we do not reuse a code index for staging until we are sure
 *      that no executing BIFs are still referencing it.
 *      We could get by with only two (0 and 1), but that would require that we
 *      must wait for all schedulers to re-schedule before each code change
 *      operation can start staging.
 *
 *      Note that the 'code index' is very loosely coupled to the concept of
 *      'current' and 'old' module versions. You can almost say that they are
 *      orthogonal to each other. Code index is an emulator global concept while
 *      'current' and 'old' is specific for each module.
 */

#ifndef __CODE_IX_H__
#define __CODE_IX_H__

#ifndef __SYS_H__
#  ifdef HAVE_CONFIG_H
#    include "config.h"
#  endif
#  include "sys.h"
#endif

#include "beam_opcodes.h"

#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY
#define ERL_THR_PROGRESS_TSD_TYPE_ONLY
#include "erl_thr_progress.h"
#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY

struct process;


#define ERTS_NUM_CODE_IX 3

#ifdef BEAMASM
#define ERTS_ADDRESSV_SIZE (ERTS_NUM_CODE_IX + 1)
#define ERTS_SAVE_CALLS_CODE_IX (ERTS_ADDRESSV_SIZE - 1)
#else
#define ERTS_ADDRESSV_SIZE ERTS_NUM_CODE_IX
#endif

/* This structure lets `Export` entries and `ErlFunEntry` share dispatch code,
 * which greatly improves the performance of fun calls. */
typedef struct ErtsDispatchable_ {
    ErtsCodePtr addresses[ERTS_ADDRESSV_SIZE];
} ErtsDispatchable;

typedef unsigned ErtsCodeIndex;

typedef struct ErtsCodeMFA_ {
    Eterm module;
    Eterm function;
    Uint  arity;
} ErtsCodeMFA;

/*
 * The ErtsCodeInfo structure is used both in the Export entry
 * and in the code as the function header.
 */

/* If you change the size of this, you also have to update the code
   in ops.tab to reflect the new func_info size */
typedef struct ErtsCodeInfo_ {
    /* In both the JIT and interpreter, we may jump here to raise a
     * function_clause error.
     *
     * In addition, the JIT also stores the current breakpoint flags here. */
    struct {
#ifndef BEAMASM
        BeamInstr op;
#else
        struct {
            char raise_function_clause[sizeof(BeamInstr) - 1];
            char breakpoint_flag;
        } metadata;
#endif
    } u;

    /* Trace breakpoint */
    struct generic_bp *gen_bp;
    ErtsCodeMFA mfa;
} ErtsCodeInfo;

typedef struct {
    erts_refc_t pending_schedulers;
    ErtsThrPrgrLaterOp later_op;
    UWord size;

    void (*later_function)(void *);
    void *later_data;
} ErtsCodeBarrier;

/* Get the code associated with a ErtsCodeInfo ptr. */
ERTS_GLB_INLINE
ErtsCodePtr erts_codeinfo_to_code(const ErtsCodeInfo *ci);

/* Get the ErtsCodeInfo for from a code ptr. */
ERTS_GLB_INLINE
const ErtsCodeInfo *erts_code_to_codeinfo(ErtsCodePtr I);

/* Get the code associated with a ErtsCodeMFA ptr. */
ERTS_GLB_INLINE
ErtsCodePtr erts_codemfa_to_code(const ErtsCodeMFA *mfa);

/* Get the ErtsCodeMFA from a code ptr. */
ERTS_GLB_INLINE
const ErtsCodeMFA *erts_code_to_codemfa(ErtsCodePtr I);

/* Called once at emulator initialization.
 */
void erts_code_ix_init(void);

/* Return active code index.
 * Is guaranteed to be valid until the calling BIF returns.
 * To get a consistent view of the code, only one call to erts_active_code_ix()
 * should be made and the returned ix reused within the same BIF call.
 */
ERTS_GLB_INLINE
ErtsCodeIndex erts_active_code_ix(void);

/* Return staging code ix.
 * Only used by a process performing code loading/upgrading/deleting/purging.
 * Code staging permission must be seized.
 */
ERTS_GLB_INLINE
ErtsCodeIndex erts_staging_code_ix(void);

/** @brief Try to seize exclusive code loading permission. That is, both
 * staging and modification permission.
 *
 * Main process lock (only) must be held.
 * System thread progress must not be blocked.
 * Caller must not already have the code modification or staging permissions.
 * Caller is suspended and *must* yield if 0 is returned. */
int erts_try_seize_code_load_permission(struct process* c_p);

/** @brief Release code loading permission. Resumes any suspended waiters. */
void erts_release_code_load_permission(void);

/** @brief Try to seize exclusive code staging permission. Needed for code
 * loading and purging.
 *
 * This is kept separate from code modification permission to allow tracing and
 * similar during long-running purge operations.
 *
 * * Main process lock (only) must be held.
 * * System thread progress must not be blocked.
 * * Caller is suspended and *must* yield if 0 is returned.
 * * Caller must not already have the code modification or staging permissions.
 *   
 *   That is, it is _NOT_ possible to add code modification permission when you
 *   already have staging permission. The other way around is fine however.
 */
int erts_try_seize_code_stage_permission(struct process* c_p);

/** @brief Release code stage permission. Resumes any suspended waiters. */
void erts_release_code_stage_permission(void);

/** @brief Try to seize exclusive code modification permission. Needed for
 * tracing, breakpoints, and so on.
 *
 * This used to be called code_write_permission, but was renamed to break
 * merges of code that uses the old locking paradigm.
 *
 * * Main process lock (only) must be held.
 * * System thread progress must not be blocked.
 * * Caller is suspended and *must* yield if 0 is returned.
 * * Caller must not already have the code modification permission, but may
 *   have staging permission.
 */
int erts_try_seize_code_mod_permission(struct process* c_p);

/** @brief As \c erts_try_seize_code_mod_permission but for aux work.
 *
 * System thread progress must not be blocked.
 * On success return true.
 * On failure return false and aux work func(arg) will be scheduled when
 * permission is released.
 */
int erts_try_seize_code_mod_permission_aux(void (*func)(void *),
                                           void *arg);

/** @brief Release code modification permission. Resumes any suspended
 * waiters. */
void erts_release_code_mod_permission(void);

/* Prepare the "staging area" to be a complete copy of the active code.
 *
 * Code staging permission must have been seized.
 *
 * Must be followed by calls to either "end" and "commit" or "abort" before
 * code staging permission can be released.
 */
void erts_start_staging_code_ix(int num_new);

/* End the staging.
 * Preceded by "start" and must be followed by "commit".
 */
void erts_end_staging_code_ix(void);

/* Set staging code index as new active code index.
 * Preceded by "end".
 */
void erts_commit_staging_code_ix(void);

/* Abort the staging.
 * Preceded by "start".
 */
void erts_abort_staging_code_ix(void);

#ifdef DEBUG
void erts_debug_require_code_barrier(void);
void erts_debug_check_code_barrier(void);
#endif

/* Schedules an operation to run after thread progress _and_ all schedulers
 * have issued an instruction barrier. */
void erts_schedule_code_barrier(ErtsCodeBarrier *barrier,
                                void (*later_function)(void *),
                                void *later_data);

void erts_schedule_code_barrier_cleanup(ErtsCodeBarrier *barrier,
                                        void (*later_function)(void *),
                                        void *later_data,
                                        UWord size);

/* Issues a code barrier on the current thread, as well as all managed threads
 * when they wake up after thread progress is unblocked.
 *
 * Requires that thread progress is blocked. */
void erts_blocking_code_barrier(void);

/* Helper function for the above: all managed threads should call this as soon
 * as thread progress is unblocked, _BEFORE_ updating thread progress. */
void erts_code_ix_finalize_wait(void);

#ifdef ERTS_ENABLE_LOCK_CHECK
int erts_has_code_load_permission(void);
int erts_has_code_stage_permission(void);
int erts_has_code_mod_permission(void);
#endif

/* module/function/arity can be NIL/NIL/-1 when the MFA is pointing to some
   invalid code, for instance unloaded_fun. */
#define ASSERT_MFA(MFA)                                                 \
    ASSERT((is_atom((MFA)->module) || is_nil((MFA)->module)) &&         \
           (is_atom((MFA)->function) || is_nil((MFA)->function)) &&     \
           (((MFA)->arity >= 0 && (MFA)->arity < 1024) || (MFA)->arity == -1))

extern erts_atomic32_t the_active_code_index;
extern erts_atomic32_t the_staging_code_index;

#if ERTS_GLB_INLINE_INCL_FUNC_DEF

ERTS_GLB_INLINE
ErtsCodePtr erts_codeinfo_to_code(const ErtsCodeInfo *ci)
{
#ifndef BEAMASM
    ASSERT(BeamIsOpCode(ci->u.op, op_i_func_info_IaaI) || !ci->u.op);
#endif
    ASSERT_MFA(&ci->mfa);
    return (ErtsCodePtr)&ci[1];
}

ERTS_GLB_INLINE
const ErtsCodeInfo *erts_code_to_codeinfo(ErtsCodePtr I)
{
    const ErtsCodeInfo *ci = &((const ErtsCodeInfo *)I)[-1];

#ifndef BEAMASM
    ASSERT(BeamIsOpCode(ci->u.op, op_i_func_info_IaaI) || !ci->u.op);
#endif
    ASSERT_MFA(&ci->mfa);

    return ci;
}

ERTS_GLB_INLINE
ErtsCodePtr erts_codemfa_to_code(const ErtsCodeMFA *mfa)
{
    ASSERT_MFA(mfa);
    return (ErtsCodePtr)&mfa[1];
}

ERTS_GLB_INLINE
const ErtsCodeMFA *erts_code_to_codemfa(ErtsCodePtr I)
{
    const ErtsCodeMFA *mfa = &((const ErtsCodeMFA *)I)[-1];

    ASSERT_MFA(mfa);

    return mfa;
}

ERTS_GLB_INLINE ErtsCodeIndex erts_active_code_ix(void)
{
    return erts_atomic32_read_nob(&the_active_code_index);
}
ERTS_GLB_INLINE ErtsCodeIndex erts_staging_code_ix(void)
{
    return erts_atomic32_read_nob(&the_staging_code_index);
}

#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */

#endif /* !__CODE_IX_H__ */