php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
fpm_scoreboard.c
Go to the documentation of this file.
1 /* (c) 2009 Jerome Loyet */
2
3#include "php.h"
4#include "SAPI.h"
5#include <stdio.h>
6#include <time.h>
7
8#include "fpm_config.h"
9#include "fpm_children.h"
10#include "fpm_scoreboard.h"
11#include "fpm_shm.h"
12#include "fpm_sockets.h"
13#include "fpm_worker_pool.h"
14#include "fpm_clock.h"
15#include "zlog.h"
16
17static struct fpm_scoreboard_s *fpm_scoreboard = NULL;
18static int fpm_scoreboard_i = -1;
19#ifdef HAVE_TIMES
20static float fpm_scoreboard_tick;
21#endif
22
23
25{
26 struct fpm_worker_pool_s *wp;
27
28#ifdef HAVE_TIMES
29#ifdef _SC_CLK_TCK
30 fpm_scoreboard_tick = sysconf(_SC_CLK_TCK);
31#else /* _SC_CLK_TCK */
32#ifdef HZ
33 fpm_scoreboard_tick = HZ;
34#else /* HZ */
35 fpm_scoreboard_tick = 100;
36#endif /* HZ */
37#endif /* _SC_CLK_TCK */
38 zlog(ZLOG_DEBUG, "got clock tick '%.0f'", fpm_scoreboard_tick);
39#endif /* HAVE_TIMES */
40
41
42 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
43 size_t scoreboard_procs_size;
44 void *shm_mem;
45
46 if (wp->config->pm_max_children < 1) {
47 zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because max_client is not set", wp->config->name);
48 return -1;
49 }
50
51 if (wp->scoreboard) {
52 zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because it already exists", wp->config->name);
53 return -1;
54 }
55
56 scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
57 shm_mem = fpm_shm_alloc(sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
58
59 if (!shm_mem) {
60 return -1;
61 }
62 wp->scoreboard = shm_mem;
63 wp->scoreboard->pm = wp->config->pm;
66 strlcpy(wp->scoreboard->pool, wp->config->name, sizeof(wp->scoreboard->pool));
67
68 if (wp->shared) {
69 /* shared pool is added after non shared ones so the shared scoreboard is allocated */
71 }
72 }
73 return 0;
74}
75
76static inline void fpm_scoreboard_readers_decrement(struct fpm_scoreboard_s *scoreboard)
77{
78 fpm_spinlock(&scoreboard->lock, 1);
79 if (scoreboard->reader_count > 0) {
80 scoreboard->reader_count -= 1;
81 }
82#ifdef PHP_FPM_ZLOG_TRACE
83 unsigned int current_reader_count = scoreboard->reader_count;
84#endif
85 fpm_unlock(scoreboard->lock);
86#ifdef PHP_FPM_ZLOG_TRACE
87 /* this is useful for debugging but currently needs to be hidden as external logger would always log it */
88 zlog(ZLOG_DEBUG, "scoreboard: for proc %d reader decremented to value %u", getpid(), current_reader_count);
89#endif
90}
91
92static struct fpm_scoreboard_s *fpm_scoreboard_get_for_update(struct fpm_scoreboard_s *scoreboard) /* {{{ */
93{
94 if (!scoreboard) {
95 scoreboard = fpm_scoreboard;
96 }
97 if (!scoreboard) {
98 zlog(ZLOG_WARNING, "Unable to update scoreboard: the SHM has not been found");
99 }
100
101 return scoreboard;
102}
103/* }}} */
104
105void fpm_scoreboard_update_begin(struct fpm_scoreboard_s *scoreboard) /* {{{ */
106{
107 scoreboard = fpm_scoreboard_get_for_update(scoreboard);
108 if (!scoreboard) {
109 return;
110 }
111
112 int retries = 0;
113 while (1) {
114 fpm_spinlock(&scoreboard->lock, 1);
115 if (scoreboard->reader_count == 0) {
116 if (!fpm_spinlock_with_max_retries(&scoreboard->writer_active, FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES)) {
117 /* in this case the writer might have crashed so just warn and continue as the lock was acquired */
118 zlog(ZLOG_WARNING, "scoreboard: writer %d waited too long for another writer to release lock.", getpid());
119 }
120#ifdef PHP_FPM_ZLOG_TRACE
121 else {
122 zlog(ZLOG_DEBUG, "scoreboard: writer lock acquired by writer %d", getpid());
123 }
124#endif
125 fpm_unlock(scoreboard->lock);
126 break;
127 }
128 fpm_unlock(scoreboard->lock);
129
130 if (++retries > FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES) {
131 /* decrement reader count by 1 (assuming a killed or crashed reader) */
132 fpm_scoreboard_readers_decrement(scoreboard);
133 zlog(ZLOG_WARNING, "scoreboard: writer detected a potential crashed reader, decrementing reader count.");
134 retries = 0;
135 }
136
137 sched_yield();
138 }
139}
140/* }}} */
141
143 int idle, int active, int lq, int lq_len, int requests, int max_children_reached,
144 int slow_rq, size_t memory_peak, int action, struct fpm_scoreboard_s *scoreboard) /* {{{ */
145{
146 scoreboard = fpm_scoreboard_get_for_update(scoreboard);
147 if (!scoreboard) {
148 return;
149 }
150
151 if (action == FPM_SCOREBOARD_ACTION_SET) {
152 if (idle >= 0) {
153 scoreboard->idle = idle;
154 }
155 if (active >= 0) {
156 scoreboard->active = active;
157 }
158 if (lq >= 0) {
159 scoreboard->lq = lq;
160 }
161 if (lq_len >= 0) {
162 scoreboard->lq_len = lq_len;
163 }
164#if HAVE_FPM_LQ /* prevent unnecessary test */
165 if (scoreboard->lq > scoreboard->lq_max) {
166 scoreboard->lq_max = scoreboard->lq;
167 }
168#endif
169 if (requests >= 0) {
170 scoreboard->requests = requests;
171 }
172
173 if (max_children_reached >= 0) {
175 }
176 if (slow_rq > 0) {
177 scoreboard->slow_rq = slow_rq;
178 }
179 } else {
180 if (scoreboard->idle + idle > 0) {
181 scoreboard->idle += idle;
182 } else {
183 scoreboard->idle = 0;
184 }
185
186 if (scoreboard->active + active > 0) {
187 scoreboard->active += active;
188 } else {
189 scoreboard->active = 0;
190 }
191
192 if (scoreboard->requests + requests > 0) {
193 scoreboard->requests += requests;
194 } else {
195 scoreboard->requests = 0;
196 }
197
198 if (scoreboard->max_children_reached + max_children_reached > 0) {
200 } else {
201 scoreboard->max_children_reached = 0;
202 }
203
204 if (scoreboard->slow_rq + slow_rq > 0) {
205 scoreboard->slow_rq += slow_rq;
206 } else {
207 scoreboard->slow_rq = 0;
208 }
209 }
210
211 if (scoreboard->active > scoreboard->active_max) {
212 scoreboard->active_max = scoreboard->active;
213 }
214 if (scoreboard->memory_peak < memory_peak) {
215 scoreboard->memory_peak = memory_peak;
216 }
217
218 fpm_unlock(scoreboard->writer_active);
219#ifdef PHP_FPM_ZLOG_TRACE
220 zlog(ZLOG_DEBUG, "scoreboard: writer lock released by writer %d", getpid());
221#endif
222}
223/* }}} */
224
225
227 int idle, int active, int lq, int lq_len, int requests, int max_children_reached,
228 int slow_rq, size_t memory_peak, int action, struct fpm_scoreboard_s *scoreboard) /* {{{ */
229{
230 fpm_scoreboard_update_begin(scoreboard);
233}
234/* }}} */
235
237{
238 return fpm_scoreboard;
239}
240
241static inline struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_ex(
242 struct fpm_scoreboard_s *scoreboard, int child_index, unsigned int nprocs) /* {{{*/
243{
244 if (!scoreboard) {
245 return NULL;
246 }
247
248 if (child_index < 0 || (unsigned int)child_index >= nprocs) {
249 return NULL;
250 }
251
252 return &scoreboard->procs[child_index];
253}
254/* }}} */
255
257 struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
258{
259 if (!scoreboard) {
260 scoreboard = fpm_scoreboard;
261 }
262
263 if (child_index < 0) {
264 child_index = fpm_scoreboard_i;
265 }
266
267 return fpm_scoreboard_proc_get_ex(scoreboard, child_index, scoreboard->nprocs);
268}
269/* }}} */
270
272{
273 struct fpm_worker_pool_s *wp = child->wp;
274 unsigned int nprocs = wp->config->pm_max_children;
275 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
276 int child_index = child->scoreboard_i;
277
278 return fpm_scoreboard_proc_get_ex(scoreboard, child_index, nprocs);
279}
280/* }}} */
281
282
283struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */
284{
285 struct fpm_scoreboard_s *s = scoreboard ? scoreboard : fpm_scoreboard;
286 if (!s) {
287 return NULL;
288 }
289
290 int retries = 0;
291 while (1) {
292 /* increment reader if no writer active */
293 fpm_spinlock(&s->lock, 1);
294 if (!s->writer_active) {
295 s->reader_count += 1;
296#ifdef PHP_FPM_ZLOG_TRACE
297 unsigned int current_reader_count = s->reader_count;
298#endif
299 fpm_unlock(s->lock);
300#ifdef PHP_FPM_ZLOG_TRACE
301 zlog(ZLOG_DEBUG, "scoreboard: for proc %d reader incremented to value %u", getpid(), current_reader_count);
302#endif
303 break;
304 }
305 fpm_unlock(s->lock);
306
307 sched_yield();
308
309 if (++retries > FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES) {
310 zlog(ZLOG_WARNING, "scoreboard: reader waited too long for writer to release lock.");
311 fpm_scoreboard_readers_decrement(s);
312 return NULL;
313 }
314 }
315
316 return s;
317}
318/* }}} */
319
320void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard) {
321 if (!scoreboard) {
322 return;
323 }
324
325 fpm_scoreboard_readers_decrement(scoreboard);
326}
327
328struct fpm_scoreboard_s *fpm_scoreboard_copy(struct fpm_scoreboard_s *scoreboard, int copy_procs)
329{
330 struct fpm_scoreboard_s *scoreboard_copy;
331 struct fpm_scoreboard_proc_s *scoreboard_proc_p;
332 size_t scoreboard_size, scoreboard_nprocs_size;
333 int i;
334 void *mem;
335
336 if (!scoreboard) {
337 scoreboard = fpm_scoreboard_get();
338 }
339
340 if (copy_procs) {
341 scoreboard_size = sizeof(struct fpm_scoreboard_s);
342 scoreboard_nprocs_size = sizeof(struct fpm_scoreboard_proc_s) * scoreboard->nprocs;
343
344 mem = malloc(scoreboard_size + scoreboard_nprocs_size);
345 } else {
346 mem = malloc(sizeof(struct fpm_scoreboard_s));
347 }
348
349 if (!mem) {
350 zlog(ZLOG_ERROR, "scoreboard: failed to allocate memory for copy");
351 return NULL;
352 }
353
354 scoreboard_copy = mem;
355
356 scoreboard = fpm_scoreboard_acquire(scoreboard, FPM_SCOREBOARD_LOCK_NOHANG);
357 if (!scoreboard) {
358 free(mem);
359 zlog(ZLOG_ERROR, "scoreboard: failed to lock (already locked)");
360 return NULL;
361 }
362
363 *scoreboard_copy = *scoreboard;
364
365 if (copy_procs) {
366 mem += scoreboard_size;
367
368 for (i = 0; i < scoreboard->nprocs; i++, mem += sizeof(struct fpm_scoreboard_proc_s)) {
369 scoreboard_proc_p = fpm_scoreboard_proc_acquire(scoreboard, i, FPM_SCOREBOARD_LOCK_HANG);
370 scoreboard_copy->procs[i] = *scoreboard_proc_p;
371 fpm_scoreboard_proc_release(scoreboard_proc_p);
372 }
373 }
374
375 fpm_scoreboard_release(scoreboard);
376
377 return scoreboard_copy;
378}
379
381{
382 free(scoreboard);
383}
384
385struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang) /* {{{ */
386{
387 struct fpm_scoreboard_proc_s *proc;
388
389 proc = fpm_scoreboard_proc_get(scoreboard, child_index);
390 if (!proc) {
391 return NULL;
392 }
393
394 if (!fpm_spinlock(&proc->lock, nohang)) {
395 return NULL;
396 }
397
398 return proc;
399}
400/* }}} */
401
403{
404 if (!proc) {
405 return;
406 }
407
408 proc->lock = 0;
409}
410
411void fpm_scoreboard_free(struct fpm_worker_pool_s *wp) /* {{{ */
412{
413 size_t scoreboard_procs_size;
414 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
415
416 if (!scoreboard) {
417 zlog(ZLOG_ERROR, "**scoreboard is NULL");
418 return;
419 }
420
421 scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
422
423 fpm_shm_free(scoreboard, sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
424}
425/* }}} */
426
427void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid) /* {{{ */
428{
429 struct fpm_scoreboard_proc_s *proc;
430 fpm_scoreboard = child->wp->scoreboard;
431 fpm_scoreboard_i = child->scoreboard_i;
433 if (!proc) {
434 return;
435 }
436 proc->pid = pid;
437 proc->start_epoch = time(NULL);
438}
439/* }}} */
440
441void fpm_scoreboard_proc_free(struct fpm_child_s *child) /* {{{ */
442{
443 struct fpm_worker_pool_s *wp = child->wp;
444 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
445 int child_index = child->scoreboard_i;
446
447 if (!scoreboard) {
448 return;
449 }
450
451 if (child_index < 0 || child_index >= wp->config->pm_max_children) {
452 return;
453 }
454
455 if (scoreboard->procs[child_index].used > 0) {
456 memset(&scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
457 }
458
459 /* set this slot as free to avoid search on next alloc */
460 scoreboard->free_proc = child_index;
461}
462/* }}} */
463
464int fpm_scoreboard_proc_alloc(struct fpm_child_s *child) /* {{{ */
465{
466 int i = -1;
467 struct fpm_worker_pool_s *wp = child->wp;
468 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
469 int nprocs = wp->config->pm_max_children;
470
471 if (!scoreboard) {
472 return -1;
473 }
474
475 /* first try the slot which is supposed to be free */
476 if (scoreboard->free_proc >= 0 && scoreboard->free_proc < nprocs) {
477 if (!scoreboard->procs[scoreboard->free_proc].used) {
478 i = scoreboard->free_proc;
479 }
480 }
481
482 if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
483 zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
484 for (i = 0; i < nprocs; i++) {
485 if (!scoreboard->procs[i].used) { /* found */
486 break;
487 }
488 }
489 }
490
491 /* no free slot */
492 if (i < 0 || i >= nprocs) {
493 zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
494 return -1;
495 }
496
497 scoreboard->procs[i].used = 1;
498 child->scoreboard_i = i;
499
500 /* supposed next slot is free */
501 if (i + 1 >= nprocs) {
502 scoreboard->free_proc = 0;
503 } else {
504 scoreboard->free_proc = i + 1;
505 }
506
507 return 0;
508}
509/* }}} */
510
511#ifdef HAVE_TIMES
512float fpm_scoreboard_get_tick(void)
513{
514 return fpm_scoreboard_tick;
515}
516#endif
char s[4]
Definition cdf.c:77
memset(ptr, 0, type->size)
#define fpm_unlock(lock)
Definition fpm_atomic.h:178
struct fpm_scoreboard_proc_s * fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang)
void fpm_scoreboard_free(struct fpm_worker_pool_s *wp)
void fpm_scoreboard_free_copy(struct fpm_scoreboard_s *scoreboard)
int fpm_scoreboard_init_main(void)
void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard)
struct fpm_scoreboard_proc_s * fpm_scoreboard_proc_get_from_child(struct fpm_child_s *child)
void fpm_scoreboard_update_begin(struct fpm_scoreboard_s *scoreboard)
void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc)
void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid)
void fpm_scoreboard_update_commit(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, size_t memory_peak, int action, struct fpm_scoreboard_s *scoreboard)
struct fpm_scoreboard_s * fpm_scoreboard_get(void)
struct fpm_scoreboard_proc_s * fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index)
struct fpm_scoreboard_s * fpm_scoreboard_copy(struct fpm_scoreboard_s *scoreboard, int copy_procs)
void fpm_scoreboard_proc_free(struct fpm_child_s *child)
int fpm_scoreboard_proc_alloc(struct fpm_child_s *child)
void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, size_t memory_peak, int action, struct fpm_scoreboard_s *scoreboard)
struct fpm_scoreboard_s * fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang)
#define FPM_SCOREBOARD_LOCK_NOHANG
#define FPM_SCOREBOARD_ACTION_SET
struct fpm_scoreboard_proc_s * fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang)
#define FPM_SCOREBOARD_SPINLOCK_MAX_RETRIES
void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc)
#define FPM_SCOREBOARD_LOCK_HANG
int fpm_shm_free(void *mem, size_t size)
Definition fpm_shm.c:41
void * fpm_shm_alloc(size_t size)
Definition fpm_shm.c:18
struct fpm_worker_pool_s * fpm_worker_all_pools
#define NULL
Definition gdcache.h:45
#define strlcpy
Definition php.h:159
time()
php_output_handler * active
Definition php_output.h:140
struct fpm_worker_pool_s * wp
unsigned int nprocs
unsigned int max_children_reached
unsigned int reader_count
struct fpm_scoreboard_s * shared
unsigned long int slow_rq
unsigned int lq_len
unsigned long int requests
struct fpm_worker_pool_config_s * config
struct fpm_worker_pool_s * shared
struct fpm_scoreboard_s * scoreboard
struct fpm_worker_pool_s * next
@ ZLOG_DEBUG
Definition zlog.h:42
@ ZLOG_ERROR
Definition zlog.h:45
@ ZLOG_WARNING
Definition zlog.h:44
#define zlog(flags,...)
Definition zlog.h:9