• 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

98.91
/src/parallel.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_PARALLEL
19
#define HAVE_PARALLEL_PARALLEL
20

21
#include "parallel.h"
22

23
/* {{{ */
24
TSRM_TLS HashTable php_parallel_runtimes;
25

26
static struct {
27
    pthread_mutex_t mutex;
28
    zend_string *bootstrap;
29
    volatile zend_ulong running;
30
} php_parallel_globals;
31

32
#define PCG(e) php_parallel_globals.e
33
/* }}} */
34

35
/* {{{ */
36
typedef int (*php_sapi_deactivate_t)(void);
37
typedef size_t (*php_sapi_output_t)(const char *, size_t);
38

39
static php_sapi_deactivate_t php_sapi_deactivate_function;
40
static php_sapi_output_t php_sapi_output_function;
41

42
static pthread_mutex_t php_parallel_output_mutex = PTHREAD_MUTEX_INITIALIZER;
43

44
static size_t php_parallel_output_function(const char *str, size_t len)
25,870✔
45
{
46
    size_t result;
25,870✔
47

48
    pthread_mutex_lock(&php_parallel_output_mutex);
25,870✔
49
    result = php_sapi_output_function(str, len);
25,870✔
50
    pthread_mutex_unlock(&php_parallel_output_mutex);
25,870✔
51

52
    return result;
25,870✔
53
} /* }}} */
54

55
/* {{{ */
56
ZEND_BEGIN_ARG_INFO_EX(php_parallel_bootstrap_arginfo, 0, 0, 1)
57
ZEND_ARG_TYPE_INFO(0, file, IS_STRING, 0)
58
ZEND_END_ARG_INFO()
59

60
static PHP_NAMED_FUNCTION(php_parallel_bootstrap)
162✔
61
{
62
    zend_string *bootstrap;
162✔
63

64
    ZEND_PARSE_PARAMETERS_START(1, 1)
162✔
65
    Z_PARAM_PATH_STR(bootstrap)
324✔
66
    ZEND_PARSE_PARAMETERS_END();
216✔
67

68
    pthread_mutex_lock(&PCG(mutex));
162✔
69

70
    if (PCG(bootstrap)) {
162✔
71
        php_parallel_exception_ex(php_parallel_runtime_error_bootstrap_ce, "\\parallel\\bootstrap already set to %s",
18✔
72
                                  ZSTR_VAL(PCG(bootstrap)));
73
        pthread_mutex_unlock(&PCG(mutex));
18✔
74
        return;
18✔
75
    }
76

77
    if (PCG(running)) {
144✔
78
        php_parallel_exception_ex(php_parallel_runtime_error_bootstrap_ce,
36✔
79
                                  "\\parallel\\bootstrap should be called once, "
80
                                  "before any calls to \\parallel\\run");
81
        pthread_mutex_unlock(&PCG(mutex));
36✔
82
        return;
36✔
83
    }
84

85
    PCG(bootstrap) = php_parallel_copy_string_interned(bootstrap);
108✔
86
    pthread_mutex_unlock(&PCG(mutex));
108✔
87
} /* }}} */
88

89
// This will return an idle runtime (aka thread) or spawn a new one, bootstap it
90
// and return that. This method is solely called by the userlands
91
// `\parallel\run()` function call.
92
/* {{{ */
93
static zend_always_inline php_parallel_runtime_t *php_parallel_runtimes_fetch()
504✔
94
{
95
    php_parallel_runtime_t *runtime;
504✔
96

97
    ZEND_HASH_FOREACH_PTR(&php_parallel_runtimes, runtime)
1,368✔
98
    {
99
        if (!php_parallel_scheduler_busy(runtime)) {
882✔
100
            return runtime;
101
        }
102
    }
103
    ZEND_HASH_FOREACH_END();
104

105
    if (!(runtime = php_parallel_runtime_construct(PCG(bootstrap)))) {
486✔
106
        return NULL;
107
    }
108

109
    pthread_mutex_lock(&PCG(mutex));
486✔
110
    PCG(running)++;
486✔
111
    pthread_mutex_unlock(&PCG(mutex));
486✔
112

113
    return zend_hash_next_index_insert_ptr(&php_parallel_runtimes, runtime);
972✔
114
}
115

116
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(php_parallel_run_arginfo, 0, 1, \\parallel\\Future, 1)
117
ZEND_ARG_OBJ_INFO(0, task, Closure, 0)
118
ZEND_ARG_TYPE_INFO(0, argv, IS_ARRAY, 0)
119
ZEND_END_ARG_INFO()
120

121
static PHP_NAMED_FUNCTION(php_parallel_run)
504✔
122
{
123
    php_parallel_runtime_t *runtime;
504✔
124
    zval *closure = NULL;
504✔
125
    zval *argv = NULL;
504✔
126

127
    ZEND_PARSE_PARAMETERS_START(1, 2)
504✔
128
    Z_PARAM_OBJECT_OF_CLASS(closure, zend_ce_closure)
1,008✔
129
    Z_PARAM_OPTIONAL
504✔
130
    Z_PARAM_ARRAY(argv)
792✔
131
    ZEND_PARSE_PARAMETERS_END();
504✔
132

133
    runtime = php_parallel_runtimes_fetch();
504✔
134

135
    if (!EG(exception)) {
504✔
136
        php_parallel_scheduler_push(runtime, closure, argv, return_value);
504✔
137
    }
138
} /* }}} */
139

140
#ifdef ZEND_DEBUG
141
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_parallel_count_arginfo, 0, 0, IS_LONG, 0)
142
ZEND_END_ARG_INFO()
143

NEW
144
PHP_NAMED_FUNCTION(php_parallel_count) { RETURN_LONG(zend_hash_num_elements(&php_parallel_runtimes)); }
×
145
#endif
146

147
/* {{{ */
148
zend_function_entry php_parallel_functions[] = {
149
    ZEND_NS_FENTRY("parallel", bootstrap, php_parallel_bootstrap, php_parallel_bootstrap_arginfo, 0)
150
        ZEND_NS_FENTRY("parallel", run, php_parallel_run, php_parallel_run_arginfo, 0)
151
#ifdef ZEND_DEBUG
152
            ZEND_NS_FENTRY("parallel", count, php_parallel_count, php_parallel_count_arginfo, 0)
153
#endif
154
                PHP_FE_END}; /* }}} */
155

156
PHP_MINIT_FUNCTION(PARALLEL_CORE)
3,168✔
157
{
158
    if (strncmp(sapi_module.name, "cli", sizeof("cli") - 1) == SUCCESS) {
3,168✔
159
        php_sapi_deactivate_function = sapi_module.deactivate;
3,168✔
160

161
        sapi_module.deactivate = NULL;
3,168✔
162
    }
163

164
    memset(&php_parallel_globals, 0, sizeof(php_parallel_globals));
3,168✔
165

166
    php_sapi_output_function = sapi_module.ub_write;
3,168✔
167

168
    sapi_module.ub_write = php_parallel_output_function;
3,168✔
169

170
    PHP_MINIT(PARALLEL_HANDLERS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
171
    PHP_MINIT(PARALLEL_EXCEPTIONS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
172
    PHP_MINIT(PARALLEL_COPY)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
173
    PHP_MINIT(PARALLEL_SCHEDULER)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
174
    PHP_MINIT(PARALLEL_CHANNEL)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
175
    PHP_MINIT(PARALLEL_EVENTS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
176
    PHP_MINIT(PARALLEL_SYNC)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
177

178
    php_parallel_mutex_init(&PCG(mutex), 1);
3,168✔
179
    PCG(running) = 0;
3,168✔
180
    PCG(bootstrap) = NULL;
3,168✔
181

182
    return SUCCESS;
3,168✔
183
}
184

185
PHP_MSHUTDOWN_FUNCTION(PARALLEL_CORE)
3,168✔
186
{
187
    PHP_MSHUTDOWN(PARALLEL_SYNC)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
188
    PHP_MSHUTDOWN(PARALLEL_EVENTS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
189
    PHP_MSHUTDOWN(PARALLEL_CHANNEL)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
190
    PHP_MSHUTDOWN(PARALLEL_SCHEDULER)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
191
    PHP_MSHUTDOWN(PARALLEL_COPY)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
192
    PHP_MSHUTDOWN(PARALLEL_EXCEPTIONS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
193
    PHP_MSHUTDOWN(PARALLEL_HANDLERS)(INIT_FUNC_ARGS_PASSTHRU);
3,168✔
194

195
    php_parallel_mutex_destroy(&PCG(mutex));
3,168✔
196

197
    if (strncmp(sapi_module.name, "cli", sizeof("cli") - 1) == SUCCESS) {
3,168✔
198
        sapi_module.deactivate = php_sapi_deactivate_function;
3,168✔
199
    }
200

201
    sapi_module.ub_write = php_sapi_output_function;
3,168✔
202

203
    return SUCCESS;
3,168✔
204
}
205

206
static void php_parallel_runtimes_release(zval *zv)
486✔
207
{
208
    php_parallel_runtime_t *runtime = (php_parallel_runtime_t *)Z_PTR_P(zv);
486✔
209

210
    OBJ_RELEASE(&runtime->std);
486✔
211
    pthread_mutex_lock(&PCG(mutex));
486✔
212
    PCG(running)--;
486✔
213
    pthread_mutex_unlock(&PCG(mutex));
486✔
214
}
486✔
215

216
PHP_RINIT_FUNCTION(PARALLEL_CORE)
5,352✔
217
{
218
    PHP_RINIT(PARALLEL_COPY)(INIT_FUNC_ARGS_PASSTHRU);
5,352✔
219

220
    zend_hash_init(&php_parallel_runtimes, 16, NULL, php_parallel_runtimes_release, 0);
5,352✔
221

222
    return SUCCESS;
5,352✔
223
}
224

225
PHP_RSHUTDOWN_FUNCTION(PARALLEL_CORE)
5,352✔
226
{
227
    zend_hash_destroy(&php_parallel_runtimes);
5,352✔
228

229
    PHP_RSHUTDOWN(PARALLEL_COPY)(INIT_FUNC_ARGS_PASSTHRU);
5,352✔
230

231
    // In case of a `zend_bailout()` this mutex could still be locked, so we
232
    // unlock it just in case.
233
    // See https://github.com/krakjoe/parallel/issues/313 for more details
234
    if (UNEXPECTED(CG(unclean_shutdown) == 1)) {
5,352✔
235
        pthread_mutex_unlock(&php_parallel_output_mutex);
108✔
236
    }
237

238
    return SUCCESS;
5,352✔
239
}
240
#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