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

krakjoe / parallel / 20284038403

16 Dec 2025 09:59PM UTC coverage: 96.75% (-0.07%) from 96.815%
20284038403

Pull #357

github

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

2672 of 2765 new or added lines in 27 files covered. (96.64%)

1 existing line in 1 file now uncovered.

2798 of 2892 relevant lines covered (96.75%)

6636.75 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

© 2025 Coveralls, Inc