1 /*-
2 * Copyright (c) 2013-2016 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * Lifetime of an objcore:
31 * phase 0 - nonexistent
32 * phase 1 - created, but no stevedore associated
33 * phase 2 - stevedore associated, being filled out
34 * phase 3 - stable, no changes happening
35 * phase 4 - unavailable, being dismantled
36 * phase 5 - stevedore disassociated
37 * phase 6 - nonexistent
38 *
39 * 0->1 ObjNew() creates objcore
40 *
41 * 1->2 STV_NewObject() associates a stevedore
42 *
43 * 2 ObjSetState() sets state
44 * 2 ObjWaitState() waits for particular state
45 * INVALID->REQ_DONE->STREAM->FINISHED->FAILED
46 *
47 * 2 ObjGetSpace() allocates space
48 * 2 ObjExtend() commits content
49 * 2 ObjWaitExtend() waits for content - used to implement ObjIterate())
50 *
51 * 2 ObjSetAttr()
52 * 2 ObjCopyAttr()
53 * 2 ObjSetFlag()
54 * 2 ObjSetDouble()
55 * 2 ObjSetU32()
56 * 2 ObjSetU64()
57 *
58 * 2->3 ObjBocDone() Boc removed from OC, clean it up
59 *
60 * 23 ObjHasAttr()
61 * 23 ObjGetAttr()
62 * 23 ObjCheckFlag()
63 * 23 ObjGetDouble()
64 * 23 ObjGetU32()
65 * 23 ObjGetU64()
66 * 23 ObjGetLen()
67 * 23 ObjGetXID()
68 *
69 * 23 ObjIterate() ... over body
70 *
71 * 23 ObjTouch() Signal to LRU(-like) facilities
72 *
73 * 3->4 HSH_Snipe() kill if not in use
74 * 3->4 HSH_Kill() make unavailable
75 *
76 * 234 ObjSlim() Release body storage (but retain attribute storage)
77 *
78 * 4->5 ObjFreeObj() disassociates stevedore
79 *
80 * 5->6 FREE_OBJ() ...in HSH_DerefObjCore()
81 */
82
83 #include "config.h"
84
85 #include <stdlib.h>
86
87 #include "cache_varnishd.h"
88 #include "cache_obj.h"
89 #include "vend.h"
90 #include "storage/storage.h"
91
92 static const struct obj_methods *
obj_getmethods(const struct objcore * oc)93 obj_getmethods(const struct objcore *oc)
94 {
95
96 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
97 CHECK_OBJ_NOTNULL(oc->stobj->stevedore, STEVEDORE_MAGIC);
98 AN(oc->stobj->stevedore->methods);
99 return (oc->stobj->stevedore->methods);
100 }
101
102 static struct boc *
obj_newboc(void)103 obj_newboc(void)
104 {
105 struct boc *boc;
106
107 ALLOC_OBJ(boc, BOC_MAGIC);
108 AN(boc);
109 Lck_New(&boc->mtx, lck_busyobj);
110 AZ(pthread_cond_init(&boc->cond, NULL));
111 boc->refcount = 1;
112 return (boc);
113 }
114
115 static void
obj_deleteboc(struct boc ** p)116 obj_deleteboc(struct boc **p)
117 {
118 struct boc *boc;
119
120 TAKE_OBJ_NOTNULL(boc, p, BOC_MAGIC);
121 Lck_Delete(&boc->mtx);
122 AZ(pthread_cond_destroy(&boc->cond));
123 free(boc->vary);
124 FREE_OBJ(boc);
125 }
126
127 /*====================================================================
128 * ObjNew()
129 *
130 */
131
132 struct objcore *
ObjNew(const struct worker * wrk)133 ObjNew(const struct worker *wrk)
134 {
135 struct objcore *oc;
136
137 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
138
139 ALLOC_OBJ(oc, OBJCORE_MAGIC);
140 AN(oc);
141 wrk->stats->n_objectcore++;
142 oc->last_lru = NAN;
143 oc->flags = OC_F_BUSY;
144
145 oc->boc = obj_newboc();
146
147 return (oc);
148 }
149
150 /*====================================================================
151 * ObjDestroy()
152 *
153 */
154
155 void
ObjDestroy(const struct worker * wrk,struct objcore ** p)156 ObjDestroy(const struct worker *wrk, struct objcore **p)
157 {
158 struct objcore *oc;
159
160 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
161 TAKE_OBJ_NOTNULL(oc, p, OBJCORE_MAGIC);
162 if (oc->boc != NULL)
163 obj_deleteboc(&oc->boc);
164 FREE_OBJ(oc);
165 wrk->stats->n_objectcore--;
166 }
167
168 /*====================================================================
169 * ObjIterate()
170 *
171 */
172
173 int
ObjIterate(struct worker * wrk,struct objcore * oc,void * priv,objiterate_f * func,int final)174 ObjIterate(struct worker *wrk, struct objcore *oc,
175 void *priv, objiterate_f *func, int final)
176 {
177 const struct obj_methods *om = obj_getmethods(oc);
178
179 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
180 AN(func);
181 AN(om->objiterator);
182 return (om->objiterator(wrk, oc, priv, func, final));
183 }
184
185 /*====================================================================
186 * ObjGetSpace()
187 *
188 * This function returns a pointer and length of free space. If there
189 * is no free space, some will be added first.
190 *
191 * The "sz" argument is an input hint of how much space is desired.
192 */
193
194 int
ObjGetSpace(struct worker * wrk,struct objcore * oc,ssize_t * sz,uint8_t ** ptr)195 ObjGetSpace(struct worker *wrk, struct objcore *oc, ssize_t *sz, uint8_t **ptr)
196 {
197 const struct obj_methods *om = obj_getmethods(oc);
198
199 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
200 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
201 AN(sz);
202 AN(ptr);
203 assert(*sz > 0);
204
205 AN(om->objgetspace);
206 return (om->objgetspace(wrk, oc, sz, ptr));
207 }
208
209 /*====================================================================
210 * ObjExtend()
211 *
212 * This function extends the used part of the object a number of bytes
213 * into the last space returned by ObjGetSpace()
214 *
215 * The final flag must be set on the last call, and it will release any
216 * surplus space allocated.
217 */
218
219 void
ObjExtend(struct worker * wrk,struct objcore * oc,ssize_t l,int final)220 ObjExtend(struct worker *wrk, struct objcore *oc, ssize_t l, int final)
221 {
222 const struct obj_methods *om = obj_getmethods(oc);
223
224 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
225 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
226 (void)final;
227 assert(l >= 0);
228
229 Lck_Lock(&oc->boc->mtx);
230 AN(om->objextend);
231 if (l > 0) {
232 om->objextend(wrk, oc, l);
233 oc->boc->len_so_far += l;
234 AZ(pthread_cond_broadcast(&oc->boc->cond));
235 }
236 Lck_Unlock(&oc->boc->mtx);
237
238 assert(oc->boc == NULL || oc->boc->state < BOS_FINISHED);
239 if (final && om->objtrimstore != NULL)
240 om->objtrimstore(wrk, oc);
241 }
242
243 /*====================================================================
244 */
245
246 uint64_t
ObjWaitExtend(const struct worker * wrk,const struct objcore * oc,uint64_t l)247 ObjWaitExtend(const struct worker *wrk, const struct objcore *oc, uint64_t l)
248 {
249 uint64_t rv;
250
251 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
252 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
253 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
254 Lck_Lock(&oc->boc->mtx);
255 while (1) {
256 rv = oc->boc->len_so_far;
257 assert(l <= rv || oc->boc->state == BOS_FAILED);
258 if (rv > l || oc->boc->state >= BOS_FINISHED)
259 break;
260 (void)Lck_CondWait(&oc->boc->cond, &oc->boc->mtx, 0);
261 }
262 rv = oc->boc->len_so_far;
263 Lck_Unlock(&oc->boc->mtx);
264 return (rv);
265 }
266
267 /*====================================================================
268 */
269
270 void
ObjSetState(struct worker * wrk,const struct objcore * oc,enum boc_state_e next)271 ObjSetState(struct worker *wrk, const struct objcore *oc,
272 enum boc_state_e next)
273 {
274 const struct obj_methods *om;
275
276 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
277 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
278 assert(next > oc->boc->state);
279
280 CHECK_OBJ_ORNULL(oc->stobj->stevedore, STEVEDORE_MAGIC);
281 assert(next != BOS_STREAM || oc->boc->state == BOS_PREP_STREAM);
282 assert(next != BOS_FINISHED || (oc->oa_present & (1 << OA_LEN)));
283
284 if (oc->stobj->stevedore != NULL) {
285 om = oc->stobj->stevedore->methods;
286 if (om->objsetstate != NULL)
287 om->objsetstate(wrk, oc, next);
288 }
289
290 Lck_Lock(&oc->boc->mtx);
291 oc->boc->state = next;
292 AZ(pthread_cond_broadcast(&oc->boc->cond));
293 Lck_Unlock(&oc->boc->mtx);
294 }
295
296 /*====================================================================
297 */
298
299 void
ObjWaitState(const struct objcore * oc,enum boc_state_e want)300 ObjWaitState(const struct objcore *oc, enum boc_state_e want)
301 {
302
303 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
304 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
305
306 Lck_Lock(&oc->boc->mtx);
307 while (1) {
308 if (oc->boc->state >= want)
309 break;
310 (void)Lck_CondWait(&oc->boc->cond, &oc->boc->mtx, 0);
311 }
312 Lck_Unlock(&oc->boc->mtx);
313 }
314
315 /*====================================================================
316 * ObjGetlen()
317 *
318 * This is a separate function because it may need locking
319 */
320
321 uint64_t
ObjGetLen(struct worker * wrk,struct objcore * oc)322 ObjGetLen(struct worker *wrk, struct objcore *oc)
323 {
324 uint64_t len;
325
326 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
327
328 AZ(ObjGetU64(wrk, oc, OA_LEN, &len));
329 return (len);
330 }
331
332 /*====================================================================
333 * ObjSlim()
334 *
335 * Free the whatever storage can be freed, without freeing the actual
336 * object yet.
337 */
338
339 void
ObjSlim(struct worker * wrk,struct objcore * oc)340 ObjSlim(struct worker *wrk, struct objcore *oc)
341 {
342 const struct obj_methods *om = obj_getmethods(oc);
343
344 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
345
346 if (om->objslim != NULL)
347 om->objslim(wrk, oc);
348 }
349
350 /*====================================================================
351 * Called when the boc used to populate the objcore is going away.
352 * Useful for releasing any leftovers from Trim.
353 */
354
355 void
ObjBocDone(struct worker * wrk,struct objcore * oc,struct boc ** boc)356 ObjBocDone(struct worker *wrk, struct objcore *oc, struct boc **boc)
357 {
358 const struct obj_methods *m;
359
360 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
361 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
362 AN(boc);
363 CHECK_OBJ_NOTNULL(*boc, BOC_MAGIC);
364 CHECK_OBJ_ORNULL(oc->stobj->stevedore, STEVEDORE_MAGIC);
365 if (oc->stobj->stevedore != NULL) {
366 m = obj_getmethods(oc);
367 if (m->objbocdone != NULL)
368 m->objbocdone(wrk, oc, *boc);
369 }
370 obj_deleteboc(boc);
371 }
372
373 /*====================================================================
374 */
375 void
ObjFreeObj(struct worker * wrk,struct objcore * oc)376 ObjFreeObj(struct worker *wrk, struct objcore *oc)
377 {
378 const struct obj_methods *m = obj_getmethods(oc);
379
380 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
381
382 AN(m->objfree);
383 m->objfree(wrk, oc);
384 AZ(oc->stobj->stevedore);
385 }
386
387 /*====================================================================
388 * ObjHasAttr()
389 *
390 * Check if object has this attribute
391 */
392
393 int
ObjHasAttr(struct worker * wrk,struct objcore * oc,enum obj_attr attr)394 ObjHasAttr(struct worker *wrk, struct objcore *oc, enum obj_attr attr)
395 {
396
397 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
398 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
399
400 if (oc->oa_present)
401 return (oc->oa_present & (1 << attr));
402
403 /* resurrected persistent objects don't have oa_present set */
404 return (ObjGetAttr(wrk, oc, attr, NULL) != NULL ? 1 : 0);
405 }
406
407 /*====================================================================
408 * ObjGetAttr()
409 *
410 * Get an attribute of the object.
411 *
412 * Returns NULL on unset or zero length attributes and len set to
413 * zero. Returns Non-NULL otherwise and len is updated with the attributes
414 * length.
415 */
416
417 const void *
ObjGetAttr(struct worker * wrk,struct objcore * oc,enum obj_attr attr,ssize_t * len)418 ObjGetAttr(struct worker *wrk, struct objcore *oc, enum obj_attr attr,
419 ssize_t *len)
420 {
421 const struct obj_methods *om = obj_getmethods(oc);
422
423 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
424
425 AN(om->objgetattr);
426 return (om->objgetattr(wrk, oc, attr, len));
427 }
428
429 /*====================================================================
430 * ObjSetAttr()
431 *
432 * Setting fixed size attributes always succeeds.
433 *
434 * Setting a variable size attribute asserts if the combined size of the
435 * variable attributes exceeds the total variable attribute space set at
436 * object creation. If there is space it always succeeds.
437 *
438 * Setting an auxiliary attribute can fail.
439 *
440 * Resetting any variable asserts if the new length does not match the
441 * previous length exactly.
442 *
443 * If ptr is Non-NULL, it points to the new content which is copied into
444 * the attribute. Otherwise the caller will have to do the copying.
445 *
446 * Return value is non-NULL on success and NULL on failure. If ptr was
447 * non-NULL, it is an error to use the returned pointer to set the
448 * attribute data, it is only a success indicator in that case.
449 */
450
451 void *
ObjSetAttr(struct worker * wrk,struct objcore * oc,enum obj_attr attr,ssize_t len,const void * ptr)452 ObjSetAttr(struct worker *wrk, struct objcore *oc, enum obj_attr attr,
453 ssize_t len, const void *ptr)
454 {
455 const struct obj_methods *om = obj_getmethods(oc);
456 void *r;
457
458 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
459 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
460
461 AN(om->objsetattr);
462 assert((int)attr < 16);
463 r = om->objsetattr(wrk, oc, attr, len, ptr);
464 if (r)
465 oc->oa_present |= (1 << attr);
466 return (r);
467 }
468
469 /*====================================================================
470 * ObjTouch()
471 */
472
473 void
ObjTouch(struct worker * wrk,struct objcore * oc,vtim_real now)474 ObjTouch(struct worker *wrk, struct objcore *oc, vtim_real now)
475 {
476 const struct obj_methods *om = obj_getmethods(oc);
477
478 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
479 if (om->objtouch != NULL)
480 om->objtouch(wrk, oc, now);
481 }
482
483 /*====================================================================
484 * Utility functions which work on top of the previous ones
485 */
486
487 int
ObjCopyAttr(struct worker * wrk,struct objcore * oc,struct objcore * ocs,enum obj_attr attr)488 ObjCopyAttr(struct worker *wrk, struct objcore *oc, struct objcore *ocs,
489 enum obj_attr attr)
490 {
491 const void *vps;
492 void *vpd;
493 ssize_t l;
494
495 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
496 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
497 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
498 CHECK_OBJ_NOTNULL(ocs, OBJCORE_MAGIC);
499
500 vps = ObjGetAttr(wrk, ocs, attr, &l);
501 // XXX: later we want to have zero-length OA's too
502 if (vps == NULL || l <= 0)
503 return (-1);
504 vpd = ObjSetAttr(wrk, oc, attr, l, vps);
505 if (vpd == NULL)
506 return (-1);
507 return (0);
508 }
509
510 unsigned
ObjGetXID(struct worker * wrk,struct objcore * oc)511 ObjGetXID(struct worker *wrk, struct objcore *oc)
512 {
513 uint32_t u;
514
515 AZ(ObjGetU32(wrk, oc, OA_VXID, &u));
516 return (u);
517 }
518
519 /*--------------------------------------------------------------------
520 * There is no well-defined byteorder for IEEE-754 double and the
521 * correct solution (frexp(3) and manual encoding) is more work
522 * than our (weak) goal of being endian-agnostic requires at this point.
523 * We give it a shot by memcpy'ing doubles over a uint64_t and then
524 * BE encode that.
525 */
526
527 int
ObjSetDouble(struct worker * wrk,struct objcore * oc,enum obj_attr a,double t)528 ObjSetDouble(struct worker *wrk, struct objcore *oc, enum obj_attr a, double t)
529 {
530 void *vp;
531 uint64_t u;
532
533 assert(sizeof t == sizeof u);
534 memcpy(&u, &t, sizeof u);
535 vp = ObjSetAttr(wrk, oc, a, sizeof u, NULL);
536 if (vp == NULL)
537 return (-1);
538 vbe64enc(vp, u);
539 return (0);
540 }
541
542 int
ObjGetDouble(struct worker * wrk,struct objcore * oc,enum obj_attr a,double * d)543 ObjGetDouble(struct worker *wrk, struct objcore *oc, enum obj_attr a, double *d)
544 {
545 const void *vp;
546 uint64_t u;
547 ssize_t l;
548
549 assert(sizeof *d == sizeof u);
550 vp = ObjGetAttr(wrk, oc, a, &l);
551 if (vp == NULL)
552 return (-1);
553 if (d != NULL) {
554 assert(l == sizeof u);
555 u = vbe64dec(vp);
556 memcpy(d, &u, sizeof *d);
557 }
558 return (0);
559 }
560
561 /*--------------------------------------------------------------------
562 */
563
564 int
ObjSetU64(struct worker * wrk,struct objcore * oc,enum obj_attr a,uint64_t t)565 ObjSetU64(struct worker *wrk, struct objcore *oc, enum obj_attr a, uint64_t t)
566 {
567 void *vp;
568
569 vp = ObjSetAttr(wrk, oc, a, sizeof t, NULL);
570 if (vp == NULL)
571 return (-1);
572 vbe64enc(vp, t);
573 return (0);
574 }
575
576 int
ObjGetU64(struct worker * wrk,struct objcore * oc,enum obj_attr a,uint64_t * d)577 ObjGetU64(struct worker *wrk, struct objcore *oc, enum obj_attr a, uint64_t *d)
578 {
579 const void *vp;
580 ssize_t l;
581
582 vp = ObjGetAttr(wrk, oc, a, &l);
583 if (vp == NULL || l != sizeof *d)
584 return (-1);
585 if (d != NULL)
586 *d = vbe64dec(vp);
587 return (0);
588 }
589
590 int
ObjSetU32(struct worker * wrk,struct objcore * oc,enum obj_attr a,uint32_t t)591 ObjSetU32(struct worker *wrk, struct objcore *oc, enum obj_attr a, uint32_t t)
592 {
593 void *vp;
594
595 vp = ObjSetAttr(wrk, oc, a, sizeof t, NULL);
596 if (vp == NULL)
597 return (-1);
598 vbe32enc(vp, t);
599 return (0);
600 }
601
602 int
ObjGetU32(struct worker * wrk,struct objcore * oc,enum obj_attr a,uint32_t * d)603 ObjGetU32(struct worker *wrk, struct objcore *oc, enum obj_attr a, uint32_t *d)
604 {
605 const void *vp;
606 ssize_t l;
607
608 vp = ObjGetAttr(wrk, oc, a, &l);
609 if (vp == NULL || l != sizeof *d)
610 return (-1);
611 if (d != NULL)
612 *d = vbe32dec(vp);
613 return (0);
614 }
615
616 /*--------------------------------------------------------------------
617 */
618
619 int
ObjCheckFlag(struct worker * wrk,struct objcore * oc,enum obj_flags of)620 ObjCheckFlag(struct worker *wrk, struct objcore *oc, enum obj_flags of)
621 {
622 const uint8_t *fp;
623
624 fp = ObjGetAttr(wrk, oc, OA_FLAGS, NULL);
625 AN(fp);
626 return ((*fp) & of);
627 }
628
629 void
ObjSetFlag(struct worker * wrk,struct objcore * oc,enum obj_flags of,int val)630 ObjSetFlag(struct worker *wrk, struct objcore *oc, enum obj_flags of, int val)
631 {
632 uint8_t *fp;
633
634 fp = ObjSetAttr(wrk, oc, OA_FLAGS, 1, NULL);
635 AN(fp);
636 if (val)
637 (*fp) |= of;
638 else
639 (*fp) &= ~of;
640 }
641
642 /*====================================================================
643 * Object event subscribtion mechanism.
644 *
645 * XXX: it is extremely unclear what the locking circumstances are here.
646 */
647
648 struct oev_entry {
649 unsigned magic;
650 #define OEV_MAGIC 0xb0b7c5a1
651 unsigned mask;
652 obj_event_f *func;
653 void *priv;
654 VTAILQ_ENTRY(oev_entry) list;
655 };
656
657 static VTAILQ_HEAD(,oev_entry) oev_list;
658 static pthread_rwlock_t oev_rwl;
659 static unsigned oev_mask;
660
661 uintptr_t
ObjSubscribeEvents(obj_event_f * func,void * priv,unsigned mask)662 ObjSubscribeEvents(obj_event_f *func, void *priv, unsigned mask)
663 {
664 struct oev_entry *oev;
665
666 AN(func);
667 AZ(mask & ~OEV_MASK);
668
669 ALLOC_OBJ(oev, OEV_MAGIC);
670 AN(oev);
671 oev->func = func;
672 oev->priv = priv;
673 oev->mask = mask;
674 AZ(pthread_rwlock_wrlock(&oev_rwl));
675 VTAILQ_INSERT_TAIL(&oev_list, oev, list);
676 oev_mask |= mask;
677 AZ(pthread_rwlock_unlock(&oev_rwl));
678 return ((uintptr_t)oev);
679 }
680
681 void
ObjUnsubscribeEvents(uintptr_t * handle)682 ObjUnsubscribeEvents(uintptr_t *handle)
683 {
684 struct oev_entry *oev, *oev2 = NULL;
685 unsigned newmask = 0;
686
687 AN(handle);
688 AN(*handle);
689 AZ(pthread_rwlock_wrlock(&oev_rwl));
690 VTAILQ_FOREACH(oev, &oev_list, list) {
691 CHECK_OBJ_NOTNULL(oev, OEV_MAGIC);
692 if ((uintptr_t)oev == *handle)
693 oev2 = oev;
694 else
695 newmask |= oev->mask;
696 }
697 AN(oev2);
698 VTAILQ_REMOVE(&oev_list, oev2, list);
699 oev_mask = newmask;
700 AZ(newmask & ~OEV_MASK);
701 AZ(pthread_rwlock_unlock(&oev_rwl));
702 FREE_OBJ(oev2);
703 *handle = 0;
704 }
705
706 void
ObjSendEvent(struct worker * wrk,struct objcore * oc,unsigned event)707 ObjSendEvent(struct worker *wrk, struct objcore *oc, unsigned event)
708 {
709 struct oev_entry *oev;
710
711 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
712 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
713 AN(event & OEV_MASK);
714 AZ(event & ~OEV_MASK);
715 if (!(event & oev_mask))
716 return;
717
718 AZ(pthread_rwlock_rdlock(&oev_rwl));
719 VTAILQ_FOREACH(oev, &oev_list, list) {
720 CHECK_OBJ_NOTNULL(oev, OEV_MAGIC);
721 if (event & oev->mask)
722 oev->func(wrk, oev->priv, oc, event);
723 }
724 AZ(pthread_rwlock_unlock(&oev_rwl));
725
726 }
727
728 void
ObjInit(void)729 ObjInit(void)
730 {
731 VTAILQ_INIT(&oev_list);
732 AZ(pthread_rwlock_init(&oev_rwl, NULL));
733 }
734