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

krakjoe / parallel / 20162060658

12 Dec 2025 09:17AM UTC coverage: 96.815%. Remained the same
20162060658

push

github

web-flow
Refactor: Replace deprecated Zend types with standard C99 types (#353)

77 of 78 new or added lines in 15 files covered. (98.72%)

3009 of 3108 relevant lines covered (96.81%)

6869.92 hits per line

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

96.55
/src/copy.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_COPY
19
#define HAVE_PARALLEL_COPY
20

21
#include "parallel.h"
22

23
#include "php_streams.h"
24
#include "php_network.h"
25

26
TSRM_TLS struct {
27
    HashTable scope;
28
    zval      unavailable;
29
    php_parallel_copy_context_t *context;
30
} php_parallel_copy_globals;
31

32
static struct {
33
    pthread_mutex_t mutex;
34
    HashTable       table;
35
} php_parallel_copy_strings = {PTHREAD_MUTEX_INITIALIZER};
36

37
zend_class_entry* php_parallel_copy_type_unavailable_ce;
38
zend_class_entry* php_parallel_copy_object_unavailable_ce;
39

40
#define PCG(e) php_parallel_copy_globals.e
41
#define PCS(e) php_parallel_copy_strings.e
42

43
static void php_parallel_copy_zval_persistent(
44
                zval *dest, zval *source,
45
                zend_string* (*php_parallel_copy_string_func)(zend_string*),
46
                void* (*php_parallel_copy_memory_func)(void *source, zend_long size));
47

48
static const uint32_t php_parallel_copy_uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX};
49

50
static void php_parallel_copy_string_free(zval *zv) {
4,951✔
51
    free(Z_PTR_P(zv));
4,951✔
52
}
4,951✔
53

54
static zend_always_inline zend_string* php_parallel_copy_string_ex(zend_string *source, bool persistent) {
5,419✔
55
    zend_string *dest = zend_string_alloc(ZSTR_LEN(source), persistent);
10,838✔
56

57
    memcpy(ZSTR_VAL(dest),
5,419✔
58
           ZSTR_VAL(source),
5,419✔
59
           ZSTR_LEN(source));
60

61
    ZSTR_VAL(dest)[ZSTR_LEN(dest)] = 0;
5,419✔
62

63
    ZSTR_LEN(dest) = ZSTR_LEN(source);
5,419✔
64
    ZSTR_H(dest)   = ZSTR_H(source);
5,419✔
65

66
    return dest;
5,419✔
67
}
68

69
zend_string* php_parallel_copy_string_interned(zend_string *source) {
9,124✔
70
    zend_string *dest;
9,124✔
71

72
    pthread_mutex_lock(&PCS(mutex));
9,124✔
73

74
    if (!(dest = zend_hash_find_ptr(&PCS(table), source))) {
13,297✔
75

76
        dest = php_parallel_copy_string_ex(source, 1);
4,951✔
77

78
        zend_string_hash_val(dest);
4,951✔
79

80
        GC_TYPE_INFO(dest) =
4,951✔
81
            IS_STRING |
82
            ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT);
83

84
        zend_hash_add_ptr(&PCS(table), dest, dest);
4,951✔
85
    }
86

87
    pthread_mutex_unlock(&PCS(mutex));
9,124✔
88

89
    return dest;
9,124✔
90
}
91

92
static zend_always_inline void php_parallel_copy_string_dtor(zend_string *source, bool persistent) {
1,728✔
93
    if (ZSTR_IS_INTERNED(source)) {
1,728✔
94
        return;
95
    }
96

97
    if (GC_DELREF(source) == 0) {
276✔
98
        pefree(source, persistent);
276✔
99
    }
100
}
101

102
static zend_always_inline zend_string* php_parallel_copy_string_ctor(zend_string *source, bool persistent) {
2,178✔
103
    if (ZSTR_IS_INTERNED(source)) {
2,178✔
104
        return php_parallel_copy_string_interned(source);
1,710✔
105
    }
106

107
    return php_parallel_copy_string_ex(source, persistent);
870✔
108
}
109

NEW
110
zend_string* php_parallel_copy_string(zend_string *source, bool persistent) {
×
111
    return php_parallel_copy_string_ctor(source, persistent);
×
112
}
113

114
zend_class_entry* php_parallel_copy_scope(zend_class_entry *class) {
396✔
115
    zend_class_entry *scope, *exists_ce;
396✔
116

117
    if (class->ce_flags & ZEND_ACC_IMMUTABLE) {
396✔
118
        exists_ce = zend_lookup_class_ex(class->name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
170✔
119
        if (exists_ce) {
170✔
120
            return class;
121
        }
122
    }
123

124
    if ((scope = zend_hash_index_find_ptr(&PCG(scope), (zend_ulong) class))) {
306✔
125
        return scope;
126
    }
127

128
#ifdef ZSTR_HAS_CE_CACHE
129
    void *caching = NULL;
135✔
130

131
    if (ZSTR_HAS_CE_CACHE(class->name) &&
135✔
132
        ZSTR_VALID_CE_CACHE(class->name)) {
135✔
133
        caching =
135✔
134
            ZSTR_GET_CE_CACHE(class->name);
135✔
135
        ZSTR_SET_CE_CACHE(class->name, NULL);
135✔
136
    }
137
#endif
138

139
    scope = zend_lookup_class(class->name);
170✔
140

141
#ifdef ZSTR_HAS_CE_CACHE
142
    if (caching) {
135✔
143
        ZSTR_SET_CE_CACHE(class->name, caching);
144
    }
145
#endif
146

147
    if (!scope) {
170✔
148
        return php_parallel_copy_type_unavailable_ce;
×
149
    }
150

151
    return zend_hash_index_update_ptr(&PCG(scope), (zend_ulong) class, scope);
170✔
152
}
153

154
static zend_always_inline zend_long php_parallel_copy_resource_ctor(zend_resource *source, bool persistent) {
36✔
155
#ifndef _WIN32
156
    if (source->type == php_file_le_stream() ||
36✔
157
        source->type == php_file_le_pstream()) {
×
158
        int fd;
36✔
159

160
        if (php_stream_cast((php_stream*) source->ptr, PHP_STREAM_AS_FD, (void*)&fd, 0) == SUCCESS) {
36✔
161
            return (zend_long) fd;
18✔
162
        }
163
    }
164
#endif
165
    return -1;
166
}
167

168
static zend_always_inline HashTable* php_parallel_copy_hash_persistent_inline(
1,173✔
169
                HashTable *source,
170
                zend_string* (*php_parallel_copy_string_func)(zend_string*),
171
                void* (*php_parallel_copy_memory_func)(void *source, zend_long size)) {
172
    HashTable *ht;
1,173✔
173
    uint32_t idx;
1,173✔
174

175
    php_parallel_copy_context_t *context, *restore;
1,173✔
176

177
    context = php_parallel_copy_context_start(
2,346✔
178
        PHP_PARALLEL_COPY_DIRECTION_PERSISTENT,
179
        &restore);
180

181
    if ((ht = php_parallel_copy_context_find(context, source))) {
1,173✔
182
        GC_ADDREF(ht);
18✔
183
        php_parallel_copy_context_end(context, restore);
18✔
184
        return ht;
18✔
185
    } else {
186
        ht = php_parallel_copy_memory_func(source, sizeof(HashTable));
1,155✔
187
        php_parallel_copy_context_insert(
1,155✔
188
            context, source, ht);
189
    }
190

191
    // see https://github.com/krakjoe/parallel/issues/306#issuecomment-2414687880
192
    // TODO: needs fixing
193
    GC_SET_REFCOUNT(ht, 2);
1,155✔
194
    GC_SET_PERSISTENT_TYPE(ht, GC_ARRAY);
1,155✔
195
    GC_ADD_FLAGS(ht, IS_ARRAY_IMMUTABLE);
1,155✔
196

197
    ht->pDestructor = ZVAL_PTR_DTOR;
1,155✔
198

199
    ht->u.flags |= HASH_FLAG_STATIC_KEYS;
1,155✔
200
    if (ht->nNumUsed == 0) {
1,155✔
201
        ht->u.flags = HASH_FLAG_UNINITIALIZED;
12✔
202
        ht->nNextFreeElement = 0;
12✔
203
        ht->nTableMask = HT_MIN_MASK;
12✔
204
        HT_SET_DATA_ADDR(ht, &php_parallel_copy_uninitialized_bucket);
12✔
205
        php_parallel_copy_context_end(context, restore);
12✔
206
        return ht;
12✔
207
    }
208

209
#ifdef HT_PACKED_SIZE
210
    // if array is packed, copy it as packed
211
    if (HT_IS_PACKED(ht)) {
760✔
212
        HT_SET_DATA_ADDR(ht, php_parallel_copy_memory_func(HT_GET_DATA_ADDR(source), HT_PACKED_SIZE_EX(source->nTableSize, HT_MIN_MASK)));
256✔
213

214
        for (idx = 0; idx < ht->nNumUsed; idx++) {
784✔
215
            zval *zv = ht->arPacked + idx;
528✔
216

217
            if (Z_TYPE_P(zv) == IS_UNDEF)
528✔
218
                continue;
28✔
219

220
            if (Z_OPT_REFCOUNTED_P(zv)) {
500✔
221
                php_parallel_copy_zval_persistent(
184✔
222
                    zv,
223
                    zv,
224
                    php_parallel_copy_string_func,
225
                    php_parallel_copy_memory_func);
226
            }
227
        }
228
        ht->nNextFreeElement = ht->nNumUsed;
256✔
229
        php_parallel_copy_context_end(context, restore);
256✔
230
        return ht;
256✔
231
    }
232
#endif
233
    ht->nNextFreeElement = 0;
887✔
234
    ht->nInternalPointer = 0;
887✔
235

236
    HT_SET_DATA_ADDR(ht, php_parallel_copy_memory_func(HT_GET_DATA_ADDR(source), HT_SIZE(source)));
887✔
237

238
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2,324✔
239
        Bucket *p = ht->arData + idx;
1,437✔
240

241
        if (Z_TYPE(p->val) == IS_UNDEF)
1,437✔
242
            continue;
14✔
243

244
        if (p->key) {
1,423✔
245
            p->key = php_parallel_copy_string_interned(p->key);
1,113✔
246
            ht->u.flags &= ~HASH_FLAG_STATIC_KEYS;
1,113✔
247
        } else if ((zend_long) p->h >= (zend_long) ht->nNextFreeElement) {
310✔
248
            ht->nNextFreeElement = p->h + 1;
310✔
249
        }
250

251
        if (Z_OPT_REFCOUNTED(p->val)) {
1,423✔
252
            php_parallel_copy_zval_persistent(
536✔
253
                &p->val,
254
                &p->val,
255
                php_parallel_copy_string_func,
256
                php_parallel_copy_memory_func);
257
        }
258
    }
259
    php_parallel_copy_context_end(context, restore);
887✔
260
    return ht;
887✔
261
}
262

263
static zend_always_inline HashTable* php_parallel_copy_hash_thread(HashTable *source) {
1,263✔
264
    HashTable *ht;
1,263✔
265

266
    php_parallel_copy_context_t *context, *restore;
1,263✔
267

268
    context = php_parallel_copy_context_start(
2,526✔
269
        PHP_PARALLEL_COPY_DIRECTION_THREAD,
270
        &restore);
271

272
    if ((ht = php_parallel_copy_context_find(context, source))) {
1,263✔
273
        GC_ADDREF(ht);
18✔
274
        php_parallel_copy_context_end(
18✔
275
            context, restore);
276
        return ht;
18✔
277
    } else {
278
        ht = php_parallel_copy_mem(source, sizeof(HashTable), 0);
1,245✔
279
        php_parallel_copy_context_insert(
1,245✔
280
            context, source, ht);
281
    }
282

283
    GC_SET_REFCOUNT(ht, 1);
1,245✔
284
    GC_DEL_FLAGS(ht, IS_ARRAY_IMMUTABLE);
1,245✔
285

286
    GC_TYPE_INFO(ht) = GC_ARRAY;
1,245✔
287

288
    ht->pDestructor = ZVAL_PTR_DTOR;
1,245✔
289

290
    if (ht->nNumUsed == 0) {
1,245✔
291
        HT_SET_DATA_ADDR(ht, &php_parallel_copy_uninitialized_bucket);
6✔
292
        php_parallel_copy_context_end(context, restore);
6✔
293
        return ht;
6✔
294
    }
295

296
    HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht)));
1,239✔
297
    memcpy(
1,239✔
298
        HT_GET_DATA_ADDR(ht),
299
        HT_GET_DATA_ADDR(source),
1,239✔
300
        HT_HASH_SIZE(ht->nTableMask));
301
#ifdef HT_PACKED_SIZE
302
    if (HT_IS_PACKED(ht)) {
856✔
303
        zval *p = ht->arPacked,
244✔
304
        *q = source->arPacked,
244✔
305
        *p_end = p + ht->nNumUsed;
244✔
306
        for (; p < p_end; p++, q++) {
712✔
307
            *p = *q;
468✔
308
            if (Z_OPT_REFCOUNTED_P(p)) {
468✔
309
                PARALLEL_ZVAL_COPY(p, q, 0);
140✔
310
            }
311
        }
312
    } else
313
#endif
314
    if (ht->u.flags & HASH_FLAG_STATIC_KEYS) {
995✔
315
        Bucket *p = ht->arData,
251✔
316
        *q = source->arData,
251✔
317
        *p_end = p + ht->nNumUsed;
251✔
318
        for (; p < p_end; p++, q++) {
608✔
319
            *p = *q;
357✔
320
            if (Z_OPT_REFCOUNTED(p->val)) {
357✔
321
                PARALLEL_ZVAL_COPY(&p->val, &p->val, 0);
58✔
322
            }
323
        }
324
    } else {
325
        Bucket *p = ht->arData,
744✔
326
        *q = source->arData,
744✔
327
        *p_end = p + ht->nNumUsed;
744✔
328
        for (; p < p_end; p++, q++) {
1,908✔
329
            if (Z_TYPE(q->val) == IS_UNDEF) {
1,164✔
330
                ZVAL_UNDEF(&p->val);
×
331
                continue;
×
332
            }
333

334
            p->val = q->val;
1,164✔
335
            p->h = q->h;
1,164✔
336
            if (q->key) {
1,164✔
337
                p->key = php_parallel_copy_string_ctor(q->key, 0);
2,184✔
338
            } else {
339
                p->key = NULL;
72✔
340
            }
341

342
            if (Z_OPT_REFCOUNTED(p->val)) {
1,164✔
343
                PARALLEL_ZVAL_COPY(&p->val, &p->val, 0);
438✔
344
            }
345
        }
346
    }
347

348
    php_parallel_copy_context_end(context, restore);
1,239✔
349
    return ht;
1,239✔
350
}
351

352
static void* php_parallel_copy_mem_persistent(void *source, zend_long size) {
2,178✔
353
    return php_parallel_copy_mem(source, size, 1);
2,178✔
354
}
355

356
static zend_string* php_parallel_copy_string_persistent(zend_string *string) {
66✔
357
    return php_parallel_copy_string_ctor(string, 1);
66✔
358
}
359

360
HashTable *php_parallel_copy_hash_ctor(HashTable *source, bool persistent) {
2,073✔
361
    if (persistent) {
2,073✔
362
        return php_parallel_copy_hash_persistent_inline(
1,620✔
363
                source,
364
                php_parallel_copy_string_persistent,
365
                php_parallel_copy_mem_persistent);
366
    }
367
    return php_parallel_copy_hash_thread(source);
2,526✔
368
}
369

370
HashTable *php_parallel_copy_hash_persistent(HashTable *source,
363✔
371
            zend_string* (*php_parallel_copy_string_func)(zend_string*),
372
            void* (*php_parallel_copy_memory_func)(void *source, zend_long size)) {
373
        return php_parallel_copy_hash_persistent_inline(source,
363✔
374
                php_parallel_copy_string_func,
375
                php_parallel_copy_memory_func);
376
}
377

378
void php_parallel_copy_hash_dtor(HashTable *table, bool persistent) {
1,146✔
379
    // see https://github.com/krakjoe/parallel/issues/306#issuecomment-2414687880
380
    // TODO: needs fixing
381
    if (GC_DELREF(table) == (persistent ? 1 : 0)) {
1,200✔
382
        if (!persistent) {
1,110✔
383
            GC_REMOVE_FROM_BUFFER(table);
36✔
384
            GC_TYPE_INFO(table) =
36✔
385
#ifdef GC_WHITE
386
                IS_NULL | (GC_WHITE << 16);
387
#else
388
                IS_NULL;
389
#endif
390
        }
391

392
#ifdef HT_PACKED_SIZE
393
        if (HT_IS_PACKED(table)){
748✔
394
            zval *p = table->arPacked,
244✔
395
                *end = p + table->nNumUsed;
244✔
396
            while (p < end) {
748✔
397
                if (Z_OPT_REFCOUNTED_P(p)) {
504✔
398
                    PARALLEL_ZVAL_DTOR(p);
144✔
399
                }
400
                p++;
504✔
401
            }
402
        } else
403
#endif
404
        if (HT_HAS_STATIC_KEYS_ONLY(table)) {
866✔
405
            Bucket *p = table->arData,
116✔
406
                *end = p + table->nNumUsed;
116✔
407
            while (p < end) {
356✔
408
                if (Z_OPT_REFCOUNTED(p->val)) {
240✔
409
                    PARALLEL_ZVAL_DTOR(&p->val);
60✔
410
                }
411
                p++;
240✔
412
            }
413
        } else {
414
            Bucket *p = table->arData,
750✔
415
                *end = p + table->nNumUsed;
750✔
416
            while (p < end) {
2,022✔
417
                if (Z_ISUNDEF(p->val)) {
1,272✔
418
                    p++;
×
419
                    continue;
×
420
                }
421

422
                if (p->key) {
1,272✔
423
                    php_parallel_copy_string_dtor(
2,400✔
424
                        p->key,
425
                        GC_FLAGS(p->key) & IS_STR_PERSISTENT);
1,128✔
426
                }
427

428
                if (Z_OPT_REFCOUNTED(p->val)) {
1,272✔
429
                    php_parallel_copy_zval_dtor(&p->val);
438✔
430
                }
431

432
                p++;
1,272✔
433
            }
434
        }
435

436
        if (HT_GET_DATA_ADDR(table) != (void*) &php_parallel_copy_uninitialized_bucket) {
1,110✔
437
            pefree(HT_GET_DATA_ADDR(table), persistent);
1,104✔
438
        }
439

440
        pefree(table, persistent);
1,110✔
441
    }
442
}
1,146✔
443

444
static zend_always_inline zend_object* php_parallel_copy_closure_persistent(zend_object *source) {
672✔
445
    zend_closure_t *closure = (zend_closure_t*) source;
672✔
446
    zend_closure_t *copy =
672✔
447
        (zend_closure_t*)
448
            php_parallel_copy_mem(
672✔
449
                source, sizeof(zend_closure_t), 1);
450

451
    php_parallel_cache_closure(&closure->func, &copy->func);
672✔
452

453
    GC_ADD_FLAGS(&copy->std, GC_IMMUTABLE);
672✔
454

455
    copy->func.common.fn_flags |= ZEND_ACC_CLOSURE;
672✔
456

457
    if (Z_TYPE(copy->this_ptr) == IS_OBJECT) {
672✔
458
        PARALLEL_ZVAL_COPY(&copy->this_ptr, &copy->this_ptr, 1);
18✔
459
    }
460

461
    php_parallel_dependencies_store(&copy->func);
672✔
462

463
    return &copy->std;
672✔
464
}
465

466
static zend_always_inline void php_parallel_copy_closure_init_run_time_cache(zend_op_array *function) {
672✔
467
    void *rtc;
672✔
468

469
    function->fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
672✔
470
#if PHP_VERSION_ID >= 80200
471
    rtc = emalloc(function->cache_size);
448✔
472

473
    ZEND_MAP_PTR_INIT(function->run_time_cache, rtc);
448✔
474
#else
475
    rtc = emalloc(sizeof(void*) + function->cache_size);
224✔
476

477
    ZEND_MAP_PTR_INIT(function->run_time_cache, rtc);
224✔
478

479
    rtc = (char*)rtc + sizeof(void*);
224✔
480

481
    ZEND_MAP_PTR_SET(function->run_time_cache, rtc);
224✔
482
#endif
483

484
    memset(rtc, 0, function->cache_size);
672✔
485
}
486

487
static zend_always_inline zend_object* php_parallel_copy_closure_thread(zend_object *source) {
672✔
488
    zend_closure_t *copy =
672✔
489
        (zend_closure_t*)
490
            php_parallel_copy_mem(
672✔
491
                source, sizeof(zend_closure_t), 0);
492
    zend_class_entry *scope =
672✔
493
        copy->func.op_array.scope;
494
    zend_op_array *function =
672✔
495
        (zend_op_array*) &copy->func;
496

497
    GC_DEL_FLAGS(&copy->std, GC_IMMUTABLE);
672✔
498

499
    zend_object_std_init(&copy->std, zend_ce_closure);
672✔
500

501
    if (copy->called_scope &&
672✔
502
        copy->called_scope->type == ZEND_USER_CLASS) {
36✔
503
        copy->called_scope =
36✔
504
            php_parallel_copy_scope(copy->called_scope);
36✔
505
    }
506

507
    if (scope &&
672✔
508
        scope->type == ZEND_USER_CLASS) {
36✔
509
        function->scope = php_parallel_copy_scope(scope);
36✔
510
    }
511

512
    if (function->static_variables) {
672✔
513
        function->static_variables =
414✔
514
            php_parallel_copy_hash_ctor(function->static_variables, 0);
414✔
515
    }
516

517
#if PHP_VERSION_ID >= 80200
518
    ZEND_MAP_PTR_INIT(function->static_variables_ptr, function->static_variables);
448✔
519
#else
520
    ZEND_MAP_PTR_INIT(function->static_variables_ptr, &function->static_variables);
224✔
521
#endif
522

523
    php_parallel_copy_closure_init_run_time_cache(function);
672✔
524

525
    if (Z_TYPE(copy->this_ptr) == IS_OBJECT) {
672✔
526
        PARALLEL_ZVAL_COPY(&copy->this_ptr, &copy->this_ptr, 0);
18✔
527
    }
528

529
    php_parallel_dependencies_load((zend_function*) function);
672✔
530

531
    return &copy->std;
672✔
532
}
533

534
static zend_always_inline zend_object* php_parallel_copy_closure_ctor(zend_object *source, bool persistent) {
1,344✔
535
    if (persistent) {
1,344✔
536
        return php_parallel_copy_closure_persistent(source);
1,344✔
537
    }
538

539
    return php_parallel_copy_closure_thread(source);
1,344✔
540
}
541

542
static zend_always_inline void php_parallel_copy_closure_dtor(zend_object *source, bool persistent) {
696✔
543
    zend_closure_t *closure;
696✔
544

545
    if (!persistent) {
696✔
546
        OBJ_RELEASE(source);
24✔
547
        return;
548
    }
549

550
    closure = (zend_closure_t*) source;
672✔
551

552
    if (closure->func.op_array.static_variables) {
672✔
553
        php_parallel_copy_hash_dtor(
414✔
554
            closure->func.op_array.static_variables, 1);
555
    }
556

557
    if (Z_TYPE(closure->this_ptr) == IS_OBJECT) {
672✔
558
        PARALLEL_ZVAL_DTOR(&closure->this_ptr);
18✔
559
    }
560

561
    pefree(closure, 1);
672✔
562
}
563

564
static zend_always_inline zend_reference* php_parallel_copy_reference_persistent(zend_reference *source) {
54✔
565
    zend_reference *reference =
54✔
566
        php_parallel_copy_mem(source, sizeof(zend_reference), 1);
54✔
567

568
    GC_SET_REFCOUNT(reference, 1);
54✔
569
    GC_ADD_FLAGS(reference, GC_IMMUTABLE);
54✔
570

571
    PARALLEL_ZVAL_COPY(&reference->val, &source->val, 1);
54✔
572

573
    return reference;
54✔
574
}
575

576
static zend_always_inline zend_reference* php_parallel_copy_reference_thread(zend_reference *source) {
54✔
577
    zend_reference *reference =
54✔
578
        php_parallel_copy_mem(source, sizeof(zend_reference), 0);
54✔
579

580
    GC_DEL_FLAGS(reference, GC_IMMUTABLE);
54✔
581

582
    PARALLEL_ZVAL_COPY(&reference->val, &source->val, 0);
54✔
583

584
    return reference;
54✔
585
}
586

587
static zend_always_inline zend_reference* php_parallel_copy_reference_ctor(zend_reference *source, bool persistent) {
54✔
588
    if (persistent) {
54✔
589
        return php_parallel_copy_reference_persistent(source);
×
590
    }
591
    return php_parallel_copy_reference_thread(source);
54✔
592
}
593

594
static zend_always_inline void php_parallel_copy_reference_dtor(zend_reference *source, bool persistent) {
36✔
595
    if (GC_DELREF(source) == 0) {
36✔
596
        PARALLEL_ZVAL_DTOR(
36✔
597
            &source->val);
598
        pefree(source, persistent);
36✔
599
    }
600
}
601

602
static size_t _php_parallel_copy_channel_size = 0;
603

604
static zend_always_inline size_t php_parallel_copy_channel_size() {
324✔
605
    if (_php_parallel_copy_channel_size == 0) {
324✔
606
        _php_parallel_copy_channel_size =
132✔
607
            sizeof(php_parallel_channel_t) +
132✔
608
            zend_object_properties_size(php_parallel_channel_ce);
132✔
609
    }
610

611
    return _php_parallel_copy_channel_size;
324✔
612
}
613

614
static zend_always_inline zend_object* php_parallel_copy_channel_persistent(zend_object *source) {
162✔
615
    php_parallel_channel_t *channel = php_parallel_channel_fetch(source),
162✔
616
                           *dest    = php_parallel_copy_mem(channel, php_parallel_copy_channel_size(), 1);
162✔
617

618
    GC_ADD_FLAGS(&dest->std, GC_IMMUTABLE);
162✔
619

620
    dest->link = php_parallel_link_copy(channel->link);
162✔
621

622
    return &dest->std;
162✔
623
}
624

625
static zend_always_inline zend_object* php_parallel_copy_channel_thread(zend_object *source) {
162✔
626
    php_parallel_channel_t *channel = php_parallel_channel_fetch(source),
162✔
627
                           *dest    = php_parallel_copy_mem(channel, php_parallel_copy_channel_size(), 0);
162✔
628

629
    GC_DEL_FLAGS(&dest->std, GC_IMMUTABLE);
162✔
630

631
    zend_object_std_init(&dest->std, php_parallel_channel_ce);
162✔
632

633
    dest->std.handlers = &php_parallel_channel_handlers;
162✔
634

635
    dest->link = php_parallel_link_copy(channel->link);
162✔
636

637
    return &dest->std;
162✔
638
}
639

640
static zend_always_inline zend_object* php_parallel_copy_channel_ctor(zend_object *source, bool persistent) {
324✔
641
    if (persistent) {
324✔
642
        return php_parallel_copy_channel_persistent(source);
324✔
643
    }
644
    return php_parallel_copy_channel_thread(source);
324✔
645
}
646

647
static zend_always_inline void php_parallel_copy_channel_dtor(zend_object *source, bool persistent) {
180✔
648
    php_parallel_channel_t *channel = php_parallel_channel_fetch(source);
180✔
649

650
    if (!persistent) {
180✔
651
        OBJ_RELEASE(source);
18✔
652
        return;
653
    }
654

655
    php_parallel_link_destroy(channel->link);
162✔
656

657
    pefree(channel, persistent);
162✔
658
}
659

660
static size_t _php_parallel_copy_sync_size = 0;
661

662
static zend_always_inline size_t php_parallel_copy_sync_size() {
180✔
663
    if (_php_parallel_copy_sync_size == 0) {
180✔
664
        _php_parallel_copy_sync_size =
36✔
665
            sizeof(php_parallel_sync_object_t) +
36✔
666
            zend_object_properties_size(php_parallel_sync_ce);
36✔
667
    }
668

669
    return _php_parallel_copy_sync_size;
180✔
670
}
671

672
static zend_always_inline zend_object* php_parallel_copy_sync_persistent(zend_object *source) {
90✔
673
    php_parallel_sync_object_t *object  = php_parallel_sync_object_fetch(source),
90✔
674
                                 *dest    = php_parallel_copy_mem(object, php_parallel_copy_sync_size(), 1);
90✔
675

676
    GC_ADD_FLAGS(&dest->std, GC_IMMUTABLE);
90✔
677

678
    dest->sync = php_parallel_sync_copy(object->sync);
90✔
679

680
    return &dest->std;
90✔
681
}
682

683
static zend_always_inline zend_object* php_parallel_copy_sync_thread(zend_object *source) {
90✔
684
    php_parallel_sync_object_t *object = php_parallel_sync_object_fetch(source),
90✔
685
                                 *dest    = php_parallel_copy_mem(object, php_parallel_copy_sync_size(), 0);
90✔
686

687
    GC_DEL_FLAGS(&dest->std, GC_IMMUTABLE);
90✔
688

689
    zend_object_std_init(&dest->std, dest->std.ce);
90✔
690
#if PHP_VERSION_ID >= 80300
691
    dest->std.handlers = source->handlers;
45✔
692
#endif
693

694
    dest->sync = php_parallel_sync_copy(object->sync);
90✔
695

696
    return &dest->std;
90✔
697
}
698

699
static zend_always_inline zend_object* php_parallel_copy_sync_ctor(zend_object *source, bool persistent) {
180✔
700
    if (persistent) {
180✔
701
        return php_parallel_copy_sync_persistent(source);
180✔
702
    }
703
    return php_parallel_copy_sync_thread(source);
180✔
704
}
705

706
static zend_always_inline void php_parallel_copy_sync_dtor(zend_object *source, bool persistent) {
90✔
707
    php_parallel_sync_object_t *object = php_parallel_sync_object_fetch(source);
90✔
708

709
    if (!persistent) {
90✔
710
        OBJ_RELEASE(source);
×
711
        return;
712
    }
713

714
    php_parallel_sync_release(object->sync);
90✔
715

716
    pefree(object, persistent);
90✔
717
}
718

719
static zend_always_inline zend_object* php_parallel_copy_object_persistent(zend_object *source) {
306✔
720
    zend_object *dest;
306✔
721
    zend_class_entry *ce;
306✔
722

723
    if (source->ce->create_object) {
306✔
724
        ce = php_parallel_copy_object_unavailable_ce;
×
725
    } else {
726
        ce = source->ce;
727
    }
728

729
    php_parallel_copy_context_t *context, *restore;
306✔
730

731
    context = php_parallel_copy_context_start(
306✔
732
        PHP_PARALLEL_COPY_DIRECTION_PERSISTENT,
733
        &restore);
734

735
    if ((dest = php_parallel_copy_context_find(context, source))) {
306✔
736
        GC_ADDREF(dest);
36✔
737
        php_parallel_copy_context_end(context, restore);
36✔
738
        return dest;
36✔
739
    } else {
740
        dest = php_parallel_copy_mem(
270✔
741
            source,
742
            sizeof(zend_object) + zend_object_properties_size(ce), 1);
270✔
743
        php_parallel_copy_context_insert(
270✔
744
            context, source, dest);
745
    }
746

747
    GC_SET_REFCOUNT(dest, 1);
270✔
748
    GC_ADD_FLAGS(dest, GC_IMMUTABLE);
270✔
749

750
    if (ce == php_parallel_copy_object_unavailable_ce) {
270✔
751
        dest->ce = ce;
×
752
        dest->handlers = zend_get_std_object_handlers();
×
753
    }
754

755
    if (ce->default_properties_count) {
270✔
756
        zval *property   = source->properties_table,
144✔
757
             *slot       = dest->properties_table,
144✔
758
             *end        = property + source->ce->default_properties_count;
144✔
759

760
        while (property < end) {
342✔
761
            PARALLEL_ZVAL_COPY(slot, property, 1);
198✔
762
            property++;
198✔
763
            slot++;
198✔
764
        }
765
    }
766

767
    if (dest->properties) {
270✔
768
        dest->properties =
18✔
769
            php_parallel_copy_hash_ctor(source->properties, 1);
18✔
770
    }
771

772
    php_parallel_copy_context_end(context, restore);
270✔
773
    return dest;
270✔
774
}
775

776
static zend_always_inline zend_object* php_parallel_copy_object_thread(zend_object *source) {
306✔
777
    zend_object *dest;
306✔
778
    zend_class_entry *ce;
306✔
779

780
    if (source->ce->create_object) {
306✔
781
        ce = php_parallel_copy_object_unavailable_ce;
×
782
    } else {
783
        ce = php_parallel_copy_scope(source->ce);
306✔
784
    }
785

786
    php_parallel_copy_context_t *context, *restore;
306✔
787

788
    context = php_parallel_copy_context_start(
306✔
789
        PHP_PARALLEL_COPY_DIRECTION_THREAD,
790
        &restore);
791

792
    if ((dest = php_parallel_copy_context_find(context, source))) {
306✔
793
        GC_ADDREF(dest);
36✔
794
        php_parallel_copy_context_end(context, restore);
36✔
795
        return dest;
36✔
796
    } else {
797
        dest = php_parallel_copy_mem(
270✔
798
            source,
799
            sizeof(zend_object) + zend_object_properties_size(ce), 0);
270✔
800
        php_parallel_copy_context_insert(
270✔
801
            context, source, dest);
802
    }
803

804
    if (ce == php_parallel_copy_object_unavailable_ce) {
270✔
805
        dest->ce = ce;
×
806
        dest->handlers = zend_get_std_object_handlers();
×
807
    }
808

809
    zend_object_std_init(dest, ce);
270✔
810

811
    GC_DEL_FLAGS(dest, GC_IMMUTABLE);
270✔
812

813
    if (ce->default_properties_count) {
270✔
814
        zval *property   = source->properties_table,
144✔
815
             *slot       = dest->properties_table,
144✔
816
             *end        = property + source->ce->default_properties_count;
144✔
817

818
        while (property < end) {
342✔
819
            PARALLEL_ZVAL_COPY(slot, property, 0);
198✔
820
            property++;
198✔
821
            slot++;
198✔
822
        }
823
    }
824

825
    if (source->properties) {
270✔
826
        dest->properties =
18✔
827
            php_parallel_copy_hash_ctor(source->properties, 0);
18✔
828
    }
829

830
    php_parallel_copy_context_end(context, restore);
270✔
831
    return dest;
270✔
832
}
833

834
static zend_always_inline zend_object* php_parallel_copy_object_ctor(zend_object *source, bool persistent) {
2,460✔
835
    if (source->ce == zend_ce_closure) {
2,460✔
836
        return php_parallel_copy_closure_ctor(source, persistent);
1,344✔
837
    }
838

839
    if (source->ce == php_parallel_channel_ce) {
1,116✔
840
        return php_parallel_copy_channel_ctor(source, persistent);
324✔
841
    }
842

843
    if (instanceof_function(source->ce, php_parallel_sync_ce)) {
1,404✔
844
        return php_parallel_copy_sync_ctor(source, persistent);
180✔
845
    }
846

847
    if (persistent) {
612✔
848
        return php_parallel_copy_object_persistent(source);
612✔
849
    }
850

851
    return php_parallel_copy_object_thread(source);
612✔
852
}
853

854
static zend_always_inline void php_parallel_copy_object_dtor(zend_object *source, bool persistent) {
1,290✔
855
    if (source->ce == zend_ce_closure) {
1,290✔
856
        php_parallel_copy_closure_dtor(source, persistent);
696✔
857
        return;
858
    }
859

860
    if (source->ce == php_parallel_channel_ce) {
594✔
861
        php_parallel_copy_channel_dtor(source, persistent);
180✔
862
        return;
863
    }
864

865
    if (instanceof_function(source->ce, php_parallel_sync_ce)) {
738✔
866
        php_parallel_copy_sync_dtor(source, persistent);
90✔
867
        return;
868
    }
869

870
    if (!persistent) {
324✔
871
        OBJ_RELEASE(source);
36✔
872
        return;
873
    }
874

875
    if (GC_DELREF(source) == 0) {
288✔
876
        if (source->ce->default_properties_count) {
252✔
877
            zval *property = source->properties_table,
126✔
878
                 *end      = property + source->ce->default_properties_count;
126✔
879

880
            while (property < end) {
306✔
881
                PARALLEL_ZVAL_DTOR(property);
180✔
882
                property++;
180✔
883
            }
884
        }
885

886
        if (source->properties) {
252✔
887
            php_parallel_copy_hash_dtor(source->properties, 1);
18✔
888
        }
889

890
        pefree(source, 1);
252✔
891
    }
892
}
893

894
void php_parallel_copy_zval_ctor(zval *dest, zval *source, bool persistent) {
9,240✔
895
    switch (Z_TYPE_P(source)) {
9,240✔
896
        case IS_NULL:
4,884✔
897
        case IS_TRUE:
898
        case IS_FALSE:
899
        case IS_LONG:
900
        case IS_DOUBLE:
901
        case IS_UNDEF:
902
            if (source != dest) {
4,884✔
903
                *dest = *source;
4,884✔
904
            }
905
        break;
906

907
        case IS_STRING:
1,020✔
908
            ZVAL_STR(dest, php_parallel_copy_string_ctor(Z_STR_P(source), persistent));
2,040✔
909
        break;
1,020✔
910

911
        case IS_ARRAY:
786✔
912
            ZVAL_ARR(dest, php_parallel_copy_hash_ctor(Z_ARRVAL_P(source), persistent));
786✔
913
        break;
786✔
914

915
        case IS_OBJECT: {
2,460✔
916
            zend_object *copy = php_parallel_copy_object_ctor(Z_OBJ_P(source), persistent);
2,460✔
917

918
            if (!copy) {
2,460✔
919
                ZVAL_NULL(dest);
×
920
            } else {
921
                ZVAL_OBJ(dest, copy);
2,460✔
922
            }
923
        } break;
924

925
        case IS_REFERENCE:
54✔
926
            ZVAL_REF(dest, php_parallel_copy_reference_ctor(Z_REF_P(source), persistent));
54✔
927
        break;
54✔
928

929
        case IS_RESOURCE: {
36✔
930
            zend_long fd = php_parallel_copy_resource_ctor(Z_RES_P(source), persistent);
36✔
931

932
            if (fd == -1) {
36✔
933
                ZVAL_NULL(dest);
18✔
934
            } else {
935
                ZVAL_LONG(dest, fd);
18✔
936
            }
937
        } break;
938

939
        default:
×
940
            ZVAL_BOOL(dest, zend_is_true(source));
×
941
    }
942
}
9,240✔
943

944
void php_parallel_copy_zval_dtor(zval *zv) {
3,492✔
945
    switch (Z_TYPE_P(zv)) {
3,492✔
946
        case IS_ARRAY:
570✔
947
            php_parallel_copy_hash_dtor(Z_ARRVAL_P(zv), GC_FLAGS(Z_ARRVAL_P(zv)) & IS_ARRAY_IMMUTABLE);
570✔
948
        break;
570✔
949

950
        case IS_STRING:
600✔
951
            php_parallel_copy_string_dtor(Z_STR_P(zv), GC_FLAGS(Z_STR_P(zv)) & IS_STR_PERSISTENT);
600✔
952
        break;
953

954
        case IS_REFERENCE:
36✔
955
            php_parallel_copy_reference_dtor(Z_REF_P(zv), GC_FLAGS(Z_REF_P(zv)) & GC_IMMUTABLE);
36✔
956
        break;
957

958
        case IS_OBJECT:
1,290✔
959
            php_parallel_copy_object_dtor(Z_OBJ_P(zv), GC_FLAGS(Z_OBJ_P(zv)) & GC_IMMUTABLE);
1,290✔
960
        break;
961

962
        default:
996✔
963
            zval_ptr_dtor(zv);
996✔
964
    }
965
}
3,492✔
966

967
static void php_parallel_copy_zval_persistent(
720✔
968
                zval *dest, zval *source,
969
                zend_string* (*php_parallel_copy_string_func)(zend_string*),
970
                void* (*php_parallel_copy_memory_func)(void *source, zend_long size)) {
971
    if (Z_TYPE_P(source) == IS_ARRAY) {
720✔
972
        ZVAL_ARR(dest,
300✔
973
            php_parallel_copy_hash_persistent(
974
                Z_ARRVAL_P(source),
975
                php_parallel_copy_string_func,
976
                php_parallel_copy_memory_func));
977
    } else if (Z_TYPE_P(source) == IS_REFERENCE) {
420✔
978
        ZVAL_REF(dest,
108✔
979
            php_parallel_copy_reference_persistent(
980
                Z_REF_P(source)));
981
    } else if (Z_TYPE_P(source) == IS_STRING) {
366✔
982
        ZVAL_STR(dest, php_parallel_copy_string_func(Z_STR_P(source)));
198✔
983
    } else {
984
        PARALLEL_ZVAL_COPY(dest, source, 1);
234✔
985
    }
986
}
720✔
987

988
zend_function* php_parallel_copy_function(const zend_function *function, bool persistent) {
1,974✔
989
    if (persistent) {
1,974✔
990
        function =              
36✔
991
            php_parallel_cache_function(function);
36✔
992

993
        php_parallel_dependencies_store(function);
36✔
994
    } else {
995
        php_parallel_dependencies_load(function);
1,938✔
996
    }
997

998
    return (zend_function*) function;
1,974✔
999
}
1000

1001
php_parallel_copy_context_t* php_parallel_copy_context_start(
4,446✔
1002
    php_parallel_copy_direction_t direction,
1003
    php_parallel_copy_context_t **previous) {
1004

1005
    if (PCG(context) &&
4,446✔
1006
        PCG(context)->direction == direction) {
1,284✔
1007
        php_parallel_atomic_addref(
1,284✔
1008
            &PCG(context)->refcount);
1009
        return *previous = PCG(context);
1,284✔
1010
    }
1011

1012
    *previous = PCG(context);
3,162✔
1013

1014
    PCG(context) =
6,324✔
1015
        (php_parallel_copy_context_t*)
1016
            pemalloc(sizeof(php_parallel_copy_context_t), 1);
3,162✔
1017
    zend_hash_init(
3,162✔
1018
        &PCG(context)->copied, 32, NULL,
1019
        NULL, 1);
1020
    PCG(context)->refcount = 1;
3,162✔
1021
    PCG(context)->direction = direction;
3,162✔
1022

1023
    return PCG(context);
3,162✔
1024
}
1025

1026
void* php_parallel_copy_context_find(php_parallel_copy_context_t *context, void *address) {
3,048✔
1027
    return zend_hash_index_find_ptr(&context->copied, (zend_ulong) address);
3,048✔
1028
}
1029

1030
void php_parallel_copy_context_insert(php_parallel_copy_context_t *context, void *address, void *assigned) {
2,940✔
1031
    zend_hash_index_update_ptr(
2,940✔
1032
        &context->copied, (zend_ulong) address, assigned);
1033
}
2,940✔
1034

1035
void php_parallel_copy_context_end(
4,445✔
1036
    php_parallel_copy_context_t *context,
1037
    php_parallel_copy_context_t *previous) {
1038

1039
    if (php_parallel_atomic_delref(&context->refcount) == 0) {
4,445✔
1040
        zend_hash_destroy(
3,162✔
1041
            &context->copied);
1042
        pefree(context, 1);
3,162✔
1043
    }
1044

1045
    PCG(context) = previous;
4,445✔
1046
}
4,445✔
1047

1048
PHP_RINIT_FUNCTION(PARALLEL_COPY)
5,352✔
1049
{
1050
    zend_hash_init(&PCG(scope),     32, NULL, NULL, 0);
5,352✔
1051

1052
    PHP_RINIT(PARALLEL_CHECK)(INIT_FUNC_ARGS_PASSTHRU);
5,352✔
1053
    PHP_RINIT(PARALLEL_DEPENDENCIES)(INIT_FUNC_ARGS_PASSTHRU);
5,352✔
1054

1055
    object_init_ex(&PCG(unavailable), php_parallel_copy_object_unavailable_ce);
5,352✔
1056

1057
    PCG(context) = NULL;
5,352✔
1058

1059
    return SUCCESS;
5,352✔
1060
}
1061

1062
PHP_RSHUTDOWN_FUNCTION(PARALLEL_COPY)
5,352✔
1063
{
1064
    zend_hash_destroy(&PCG(scope));
5,352✔
1065

1066
    PHP_RSHUTDOWN(PARALLEL_DEPENDENCIES)(INIT_FUNC_ARGS_PASSTHRU);
5,352✔
1067
    PHP_RSHUTDOWN(PARALLEL_CHECK)(INIT_FUNC_ARGS_PASSTHRU);
5,352✔
1068

1069
    zval_ptr_dtor(&PCG(unavailable));
5,352✔
1070

1071
    return SUCCESS;
5,352✔
1072
}
1073

1074
PHP_MINIT_FUNCTION(PARALLEL_COPY_STRINGS)
3,168✔
1075
{
1076
    zend_hash_init(&PCS(table), 32, NULL, php_parallel_copy_string_free, 1);
3,168✔
1077

1078
    return SUCCESS;
3,168✔
1079
}
1080

1081
PHP_MINIT_FUNCTION(PARALLEL_COPY)
3,168✔
1082
{
1083
    zend_class_entry ce;
3,168✔
1084

1085
    INIT_NS_CLASS_ENTRY(ce, "parallel\\Runtime\\Type", "Unavailable", NULL);
3,168✔
1086

1087
    php_parallel_copy_type_unavailable_ce = zend_register_internal_class(&ce);
3,168✔
1088

1089
    INIT_NS_CLASS_ENTRY(ce, "parallel\\Runtime\\Object", "Unavailable", NULL);
3,168✔
1090

1091
    php_parallel_copy_object_unavailable_ce = zend_register_internal_class(&ce);
3,168✔
1092

1093
    PHP_MINIT(PARALLEL_DEPENDENCIES)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
1094
    PHP_MINIT(PARALLEL_CACHE)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
1095
    PHP_MINIT(PARALLEL_COPY_STRINGS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
1096

1097
    return SUCCESS;
3,168✔
1098
}
1099

1100
static PHP_MSHUTDOWN_FUNCTION(PARALLEL_COPY_STRINGS)
3,168✔
1101
{
1102
    zend_hash_destroy(&PCS(table));
3,168✔
1103

1104
    return SUCCESS;
3,168✔
1105
}
1106

1107
PHP_MSHUTDOWN_FUNCTION(PARALLEL_COPY)
3,168✔
1108
{
1109
    PHP_MSHUTDOWN(PARALLEL_CACHE)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
1110
    PHP_MSHUTDOWN(PARALLEL_DEPENDENCIES)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
1111
    PHP_MSHUTDOWN(PARALLEL_COPY_STRINGS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
1112

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

© 2026 Coveralls, Inc