• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

krakjoe / parallel / 20273776347

16 Dec 2025 03:42PM UTC coverage: 96.75% (-0.07%) from 96.815%
20273776347

Pull #357

github

web-flow
Merge 324a13a76 into 14042a874
Pull Request #357: Cleanup code formatting and docs

1527 of 1615 new or added lines in 27 files covered. (94.55%)

1 existing line in 1 file now uncovered.

2798 of 2892 relevant lines covered (96.75%)

6599.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.13
/src/cache.c
1
/*
2
  +----------------------------------------------------------------------+
3
  | parallel                                                             |
4
  +----------------------------------------------------------------------+
5
  | Copyright (c) Joe Watkins 2019-2024                                  |
6
  +----------------------------------------------------------------------+
7
  | This source file is subject to version 3.01 of the PHP license,      |
8
  | that is bundled with this package in the file LICENSE, and is        |
9
  | available through the world-wide-web at the following url:           |
10
  | http://www.php.net/license/3_01.txt                                  |
11
  | If you did not receive a copy of the PHP license and are unable to   |
12
  | obtain it through the world-wide-web, please send a note to          |
13
  | license@php.net so we can mail you a copy immediately.               |
14
  +----------------------------------------------------------------------+
15
  | Author: krakjoe                                                      |
16
  +----------------------------------------------------------------------+
17
 */
18
#ifndef HAVE_PARALLEL_CACHE
19
#define HAVE_PARALLEL_CACHE
20

21
#include "parallel.h"
22

23
static struct {
24
    pthread_mutex_t mutex;
25
    HashTable table;
26
    struct {
27
        size_t size;
28
        size_t used;
29
        void *mem;
30
        void *block;
31
    } memory;
32
} php_parallel_cache_globals = {PTHREAD_MUTEX_INITIALIZER};
33

34
#define PCG(e) php_parallel_cache_globals.e
35
#define PCM(e) PCG(memory).e
36

37
#define PARALLEL_CACHE_CHUNK PARALLEL_PLATFORM_ALIGNED((1024 * 1024) * 8)
38

39
/* {{{ */
40
static zend_always_inline void *php_parallel_cache_alloc(size_t size)
5,284✔
41
{
42
    void *mem;
5,284✔
43
    size_t aligned = PARALLEL_PLATFORM_ALIGNED(size);
5,284✔
44

45
    ZEND_ASSERT(size < PARALLEL_CACHE_CHUNK);
5,284✔
46

47
    if ((PCM(used) + aligned) >= PCM(size)) {
5,284✔
NEW
48
        PCM(size) = PARALLEL_PLATFORM_ALIGNED(PCM(size) + PARALLEL_CACHE_CHUNK);
×
NEW
49
        PCM(mem) = realloc(PCM(mem), PCM(size));
×
50

NEW
51
        if (!PCM(mem)) {
×
52
            /* out of memory */
53
            return NULL;
54
        }
55

NEW
56
        PCM(block) = (void *)(((char *)PCM(mem)) + PCM(used));
×
57
    }
58

59
    mem = PCM(block);
5,284✔
60
    PCM(block) = (void *)(((char *)PCM(block)) + aligned);
5,284✔
61
    PCM(used) += aligned;
5,284✔
62

63
    return mem;
5,284✔
64
}
65

66
static zend_always_inline void *php_parallel_cache_copy_mem(void *source, zend_long size)
4,768✔
67
{
68
    void *destination = php_parallel_cache_alloc(size);
120✔
69

70
    memcpy(destination, source, size);
4,648✔
71

72
    return destination;
4,768✔
73
} /* }}} */
74

75
static zend_always_inline HashTable *php_parallel_cache_statics(HashTable *statics)
21✔
76
{ /* {{{ */
77
    HashTable *cached = zend_hash_index_find_ptr(&PCG(table), (zend_ulong)statics);
42✔
78

79
    if (cached) {
×
80
        return cached;
81
    }
82

83
    cached = php_parallel_copy_hash_persistent(statics, php_parallel_copy_string_interned, php_parallel_cache_copy_mem);
21✔
84

85
    return zend_hash_index_update_ptr(&PCG(table), (zend_ulong)statics, cached);
21✔
86
} /* }}} */
87

88
static zend_always_inline void php_parallel_cache_type(zend_type *type)
266✔
89
{ /* {{{ */
90
    zend_type *single;
266✔
91

92
    if (!ZEND_TYPE_IS_SET(*type)) {
266✔
93
        return;
94
    }
95

96
    if (ZEND_TYPE_HAS_LIST(*type)) {
150✔
97
        zend_type_list *list = ZEND_TYPE_LIST(*type);
6✔
98

99
        list = php_parallel_cache_copy_mem(list, ZEND_TYPE_LIST_SIZE(list->num_types));
6✔
100

101
        if (ZEND_TYPE_USES_ARENA(*type)) {
6✔
102
            ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT;
5✔
103
        }
104

105
        ZEND_TYPE_SET_PTR(*type, list);
6✔
106
    }
107

108
    ZEND_TYPE_FOREACH(*type, single)
150✔
109
    {
110
        if (ZEND_TYPE_HAS_NAME(*single)) {
156✔
111
            zend_string *name = ZEND_TYPE_NAME(*single);
90✔
112

113
            ZEND_TYPE_SET_PTR(*single, php_parallel_copy_string_interned(name));
90✔
114
        }
115
    }
116
    ZEND_TYPE_FOREACH_END();
156✔
117
} /* }}} */
118

119
/* {{{ */
120
static zend_op_array *php_parallel_cache_create(const zend_function *source, bool statics)
2,451✔
121
{
122
    zend_op_array *cached = php_parallel_cache_copy_mem((void *)source, sizeof(zend_op_array));
2,451✔
123

124
    cached->fn_flags |= ZEND_ACC_IMMUTABLE;
2,451✔
125

126
    if (statics && cached->static_variables) {
2,451✔
127
        cached->static_variables = php_parallel_cache_statics(cached->static_variables);
42✔
128
    }
129

130
#if PHP_VERSION_ID >= 80200
131
    ZEND_MAP_PTR_INIT(cached->static_variables_ptr, cached->static_variables);
1,668✔
132
#else
133
    ZEND_MAP_PTR_INIT(cached->static_variables_ptr, &cached->static_variables);
783✔
134
#endif
135

136
    ZEND_MAP_PTR_INIT(cached->run_time_cache, NULL);
2,451✔
137

138
#if PHP_VERSION_ID >= 80100
139
    if (cached->num_dynamic_func_defs) {
2,085✔
140
        uint32_t it = 0;
180✔
141

142
        cached->dynamic_func_defs = php_parallel_cache_copy_mem(
360✔
143
            cached->dynamic_func_defs, sizeof(zend_op_array *) * cached->num_dynamic_func_defs);
180✔
144

145
        while (it < cached->num_dynamic_func_defs) {
390✔
146
            cached->dynamic_func_defs[it] =
420✔
147
                php_parallel_cache_create((zend_function *)cached->dynamic_func_defs[it], statics);
210✔
148
            it++;
210✔
149
        }
150
    }
151
#endif
152

153
    // This path is taken when OPcache is enabled and active, and the `source`
154
    // zend_function's opcodes are already handled and stored in OPcache's
155
    // Shared Memory (SHM). OPcache sets `op_array->refcount`to `NULL` for such
156
    // persistent `op_array`'s.
157
    if (!cached->refcount) {
2,451✔
158
        goto _php_parallel_cached_function_return;
1,616✔
159
    }
160

161
    cached->refcount = NULL;
835✔
162

163
    if (cached->last_literal) {
835✔
164
        zval *literal = cached->literals;
835✔
165
        zval *end = literal + cached->last_literal;
835✔
166
        zval *slot = php_parallel_cache_copy_mem(cached->literals, sizeof(zval) * cached->last_literal);
835✔
167

168
        cached->literals = slot;
835✔
169

170
        while (literal < end) {
4,127✔
171
            if (Z_TYPE_P(literal) == IS_ARRAY) {
3,292✔
172
                ZVAL_ARR(slot, php_parallel_copy_hash_persistent(Z_ARRVAL_P(literal), php_parallel_copy_string_interned,
42✔
173
                                                                 php_parallel_cache_copy_mem));
174
            } else if (Z_TYPE_P(literal) == IS_STRING) {
3,250✔
175
                ZVAL_STR(slot, php_parallel_copy_string_interned(Z_STR_P(literal)));
1,848✔
176
            }
177

178
            Z_TYPE_FLAGS_P(slot) &= ~(IS_TYPE_REFCOUNTED | IS_TYPE_COLLECTABLE);
3,292✔
179
            literal++;
3,292✔
180
            slot++;
3,292✔
181
        }
182
    }
183

184
    if (cached->last_var) {
835✔
185
        zend_string **vars = cached->vars;
516✔
186
        uint32_t it = 0;
516✔
187
        uint32_t end = cached->last_var;
516✔
188
        zend_string **heap = php_parallel_cache_alloc(cached->last_var * sizeof(zend_string *));
516✔
189

190
        while (it < end) {
1,227✔
191
            heap[it] = php_parallel_copy_string_interned(vars[it]);
711✔
192
            it++;
711✔
193
        }
194
        cached->vars = heap;
516✔
195
    }
196

197
    if (cached->last) {
835✔
198
        zend_op *opcodes = php_parallel_cache_copy_mem(cached->opcodes, sizeof(zend_op) * cached->last);
835✔
199
        zend_op *opline = opcodes;
835✔
200
        zend_op *end = opline + cached->last;
835✔
201

202
        while (opline < end) {
6,327✔
203
            if (opline->op1_type == IS_CONST) {
5,492✔
204
#if ZEND_USE_ABS_CONST_ADDR
205
                opline->op1.zv =
206
                    (zval *)((char *)opline->op1.zv + ((char *)cached->literals - (char *)source->op_array.literals));
207
#else
208
                opline->op1.constant =
1,755✔
209
                    (char *)(cached->literals + ((zval *)((char *)(source->op_array.opcodes + (opline - opcodes)) +
1,755✔
210
                                                          (int32_t)opline->op1.constant) -
1,755✔
211
                                                 source->op_array.literals)) -
1,755✔
212
                    (char *)opline;
213
#endif
214
                if (opline->opcode == ZEND_SEND_VAL || opline->opcode == ZEND_SEND_VAL_EX ||
1,755✔
215
                    opline->opcode == ZEND_QM_ASSIGN) {
216
                    zend_vm_set_opcode_handler_ex(opline, 0, 0, 0);
255✔
217
                }
218
            }
219
            if (opline->op2_type == IS_CONST) {
5,492✔
220
#if ZEND_USE_ABS_CONST_ADDR
221
                opline->op2.zv =
222
                    (zval *)((char *)opline->op2.zv + ((char *)cached->literals - (char *)source->op_array.literals));
223
#else
224
                opline->op2.constant =
966✔
225
                    (char *)(cached->literals + ((zval *)((char *)(source->op_array.opcodes + (opline - opcodes)) +
966✔
226
                                                          (int32_t)opline->op2.constant) -
966✔
227
                                                 source->op_array.literals)) -
966✔
228
                    (char *)opline;
229
#endif
230
            }
231
#if ZEND_USE_ABS_JMP_ADDR
232
            switch (opline->opcode) {
233
            case ZEND_JMP:
234
            case ZEND_FAST_CALL:
235
                opline->op1.jmp_addr = &opcodes[opline->op1.jmp_addr - source->op_array.opcodes];
236
                break;
237
#if PHP_VERSION_ID < 80200
238
            case ZEND_JMPZNZ:
239
#endif
240
            case ZEND_JMPZ:
241
            case ZEND_JMPNZ:
242
            case ZEND_JMPZ_EX:
243
            case ZEND_JMPNZ_EX:
244
            case ZEND_JMP_SET:
245
            case ZEND_COALESCE:
246
            case ZEND_FE_RESET_R:
247
            case ZEND_FE_RESET_RW:
248
            case ZEND_ASSERT_CHECK:
249
                opline->op2.jmp_addr = &opcodes[opline->op2.jmp_addr - source->op_array.opcodes];
250
                break;
251

252
            case ZEND_CATCH:
253
                if (!(opline->extended_value & ZEND_LAST_CATCH)) {
254
                    opline->op2.jmp_addr = &opcodes[opline->op2.jmp_addr - source->op_array.opcodes];
255
                }
256
                break;
257
            }
258
#endif
259

260
            opline++;
5,492✔
261
        }
262
        cached->opcodes = opcodes;
835✔
263
    }
264

265
    if (cached->arg_info) {
835✔
266
        zend_arg_info *it = cached->arg_info;
218✔
267
        zend_arg_info *end = it + cached->num_args;
218✔
268
        zend_arg_info *info;
218✔
269

270
        if (cached->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
218✔
271
            it--;
36✔
272
        }
273
        if (cached->fn_flags & ZEND_ACC_VARIADIC) {
218✔
274
            end++;
12✔
275
        }
276

277
        cached->arg_info = info = php_parallel_cache_copy_mem(it, (end - it) * sizeof(zend_arg_info));
218✔
278

279
        while (it < end) {
484✔
280
            if (info->name) {
266✔
281
                info->name = php_parallel_copy_string_interned(it->name);
230✔
282
            }
283

284
            php_parallel_cache_type(&info->type);
266✔
285

286
            info++;
266✔
287
            it++;
266✔
288
        }
289
        if (cached->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
218✔
290
            cached->arg_info++;
36✔
291
        }
292
    }
293

294
    if (cached->try_catch_array) {
835✔
295
        cached->try_catch_array = php_parallel_cache_copy_mem(cached->try_catch_array,
18✔
296
                                                              sizeof(zend_try_catch_element) * cached->last_try_catch);
18✔
297
    }
298

299
    if (cached->live_range) {
835✔
300
        cached->live_range =
210✔
301
            php_parallel_cache_copy_mem(cached->live_range, sizeof(zend_live_range) * cached->last_live_range);
210✔
302
    }
303

304
    if (cached->function_name) {
835✔
305
        cached->function_name = php_parallel_copy_string_interned(cached->function_name);
835✔
306
    }
307

308
    if (cached->filename) {
835✔
309
        cached->filename = php_parallel_copy_string_interned(cached->filename);
835✔
310
    }
311

312
    if (cached->doc_comment) {
835✔
313
        cached->doc_comment = php_parallel_copy_string_interned(cached->doc_comment);
6✔
314
    }
315

316
_php_parallel_cached_function_return:
829✔
317
    return cached;
2,451✔
318
} /* }}} */
319

320
/* {{{ */
321
static zend_always_inline zend_function *php_parallel_cache_function_ex(const zend_function *source, bool statics)
2,925✔
322
{
323
    zend_op_array *cached;
2,925✔
324

325
    pthread_mutex_lock(&PCG(mutex));
2,925✔
326

327
    if ((cached = zend_hash_index_find_ptr(&PCG(table), (zend_ulong)source->op_array.opcodes))) {
3,609✔
328
        goto _php_parallel_cached_function_return;
684✔
329
    }
330

331
    cached = php_parallel_cache_create(source, statics);
2,241✔
332

333
    zend_hash_index_add_ptr(&PCG(table), (zend_ulong)source->op_array.opcodes, cached);
2,241✔
334

335
_php_parallel_cached_function_return:
2,925✔
336
    pthread_mutex_unlock(&PCG(mutex));
2,925✔
337

338
    return (zend_function *)cached;
2,925✔
339
} /* }}} */
340

341
zend_function *php_parallel_cache_closure(const zend_function *source, zend_function *closure)
2,889✔
342
{ /* {{{ */
343
    zend_op_array *cache;
2,889✔
344

345
    cache = (zend_op_array *)php_parallel_cache_function_ex((zend_function *)source, 0);
2,889✔
346

347
    if (!closure) {
2,889✔
348
        closure = php_parallel_copy_mem(cache, sizeof(zend_op_array), 1);
2,217✔
349
    } else {
350
        memcpy(closure, cache, sizeof(zend_op_array));
672✔
351
    }
352

353
    if (source->op_array.static_variables) {
2,889✔
354
        HashTable *statics = ZEND_MAP_PTR_GET(source->op_array.static_variables_ptr);
693✔
355

356
        if (statics) {
693✔
357
            closure->op_array.static_variables = php_parallel_copy_hash_ctor(statics, 1);
558✔
358

359
#if PHP_VERSION_ID >= 80200
360
            ZEND_MAP_PTR_INIT(closure->op_array.static_variables_ptr, closure->op_array.static_variables);
372✔
361
#else
362
            ZEND_MAP_PTR_INIT(closure->op_array.static_variables_ptr, &closure->op_array.static_variables);
186✔
363
#endif
364
        }
365
    }
366

367
#if PHP_VERSION_ID >= 80100
368
    if (source->op_array.num_dynamic_func_defs) {
2,455✔
369
        uint32_t it = 0;
195✔
370
        /* Use regular persistent memory for dynamic_func_defs array, not cache pool */
371
        closure->op_array.dynamic_func_defs =
390✔
372
            pemalloc(sizeof(zend_op_array *) * source->op_array.num_dynamic_func_defs, 1);
195✔
373
        memcpy(closure->op_array.dynamic_func_defs, source->op_array.dynamic_func_defs,
615✔
374
               sizeof(zend_op_array *) * source->op_array.num_dynamic_func_defs);
195✔
375
        while (it < source->op_array.num_dynamic_func_defs) {
420✔
376
            closure->op_array.dynamic_func_defs[it] = (zend_op_array *)php_parallel_cache_closure(
450✔
377
                (zend_function *)source->op_array.dynamic_func_defs[it], NULL);
225✔
378
            it++;
225✔
379
        }
380
    }
381
#endif
382

383
    return closure;
2,889✔
384
} /* }}} */
385

386
zend_function *php_parallel_cache_function(const zend_function *source)
36✔
387
{ /* {{{ */
388
    return php_parallel_cache_function_ex(source, 1);
36✔
389
} /* }}} */
390

391
/* {{{ */
392
PHP_MINIT_FUNCTION(PARALLEL_CACHE)
3,168✔
393
{
394
    zend_hash_init(&PCG(table), 32, NULL, NULL, 1);
3,168✔
395

396
    PCM(size) = PARALLEL_CACHE_CHUNK;
3,168✔
397
    PCM(mem) = PCM(block) = malloc(PCM(size));
3,168✔
398

399
    if (!PCM(mem)) {
3,168✔
400
        /* out of memory */
401
    }
3,168✔
402

403
    return SUCCESS;
3,168✔
404
}
405

406
PHP_MSHUTDOWN_FUNCTION(PARALLEL_CACHE)
3,168✔
407
{
408
    zend_hash_destroy(&PCG(table));
3,168✔
409

410
    if (PCM(mem)) {
3,168✔
411
        free(PCM(mem));
3,168✔
412
    }
413

414
    return SUCCESS;
3,168✔
415
} /* }}} */
416
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc