>>>> Might I suggest a reference count? RUN_ONCE() can initialize the mutex
>>>> around the counter, and then the counter can keep track of the init's and
>>>> fini's called, and only do the work on t
>he 0->1 edge and the 1->0 edge
>>> .
>>>
>>> You also need to destroy the mutex somewhen.
>>>
>>> RUN_ONCE needs to learn a bit more about life cycles.
>>
>> I made INIT_ONCE(9) and FINI_ONCE(9) with reference counter.
>> Is this OK like this?
>
>Hi,
>
>Thanks for working on this.
>
>At the moment your _{init,fini}_once are missing cv_broadcasts, but I
>wonder if we can merge _{run,init}_once and avoid duplicate code.
>
>I'm not sure if _fini_done should ever wait.
Added tech-kern.
I fixed. thanks.
we need kernel version bump due to changing once_t and deleting _run_once().
cvs -q diff -aup sys/once.h kern/subr_once.c
Index: sys/once.h
===
RCS file: /src/cvs/cvsroot-netbsd/src/sys/sys/once.h,v
retrieving revision 1.6
diff -a -u -p -r1.6 once.h
--- sys/once.h 3 Mar 2018 19:21:59 - 1.6
+++ sys/once.h 11 Mar 2019 10:25:18 -
@@ -31,23 +31,29 @@
#define_SYS_ONCE_H_
typedef struct {
- unsigned o_status;
int o_error;
+ uint16_t o_refcnt;
+ uint16_t o_status;
#defineONCE_VIRGIN 0
#defineONCE_RUNNING1
#defineONCE_DONE 2
} once_t;
void once_init(void);
-int _run_once(once_t *, int (*)(void));
+int _init_once(once_t *, int (*)(void));
+void _fini_once(once_t *, void (*)(void));
#defineONCE_DECL(o) \
- once_t (o) __read_mostly = { \
+ once_t (o) = { \
.o_status = 0, \
+ .o_refcnt = 0, \
};
#defineRUN_ONCE(o, fn) \
(__predict_true((o)->o_status == ONCE_DONE) ? \
- ((o)->o_error) : _run_once((o), (fn)))
+ ((o)->o_error) : _init_once((o), (fn)))
+
+#defineINIT_ONCE(o, fn)_init_once((o), (fn))
+#defineFINI_ONCE(o, fn)_fini_once((o), (fn))
#endif /* _SYS_ONCE_H_ */
Index: kern/subr_once.c
===
RCS file: /src/cvs/cvsroot-netbsd/src/sys/kern/subr_once.c,v
retrieving revision 1.6
diff -a -u -p -r1.6 subr_once.c
--- kern/subr_once.c15 Mar 2009 17:14:40 - 1.6
+++ kern/subr_once.c11 Mar 2019 10:33:37 -
@@ -48,13 +48,17 @@ once_init(void)
}
int
-_run_once(once_t *o, int (*fn)(void))
+_init_once(once_t *o, int (*fn)(void))
{
-
/* Fastpath handled by RUN_ONCE() */
+ int error;
+
mutex_enter(&oncemtx);
- if (o->o_status == ONCE_VIRGIN) {
+ while (o->o_status == ONCE_RUNNING)
+ cv_wait(&oncecv, &oncemtx);
+
+ if (o->o_refcnt++ == 0) {
o->o_status = ONCE_RUNNING;
mutex_exit(&oncemtx);
o->o_error = fn();
@@ -62,10 +66,34 @@ _run_once(once_t *o, int (*fn)(void))
o->o_status = ONCE_DONE;
cv_broadcast(&oncecv);
}
- while (o->o_status != ONCE_DONE)
+ KASSERT(o->o_refcnt != 0); /* detect overflow */
+
+ while (o->o_status == ONCE_RUNNING)
cv_wait(&oncecv, &oncemtx);
+ error = o->o_error;
mutex_exit(&oncemtx);
- KASSERT(o->o_status == ONCE_DONE);
- return o->o_error;
+ return error;
+}
+
+void
+_fini_once(once_t *o, void (*fn)(void))
+{
+ mutex_enter(&oncemtx);
+ while (o->o_status == ONCE_RUNNING)
+ cv_wait(&oncecv, &oncemtx);
+
+ KASSERT(o->o_refcnt != 0); /* we need to call _init_once() once */
+ if (--o->o_refcnt == 0) {
+ o->o_status = ONCE_RUNNING;
+ mutex_exit(&oncemtx);
+ fn();
+ mutex_enter(&oncemtx);
+ o->o_status = ONCE_VIRGIN;
+ cv_broadcast(&oncecv);
+ }
+
+ while (o->o_status == ONCE_RUNNING)
+ cv_wait(&oncecv, &oncemtx);
+ mutex_exit(&oncemtx);
}
--
ryo shimizu