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