Cóż, wiesz, co mówią o konieczności bycia matką wszystkich wynalazków (lub badań w tym przypadku). Naprawdę potrzebowałem tej odpowiedzi i otrzymałem ją, przeglądając jądro/workqueue.c. Chociaż odpowiedź jest głównie zawarta w doc comments w połączeniu z Documentation/workqueue.txt
, nie jest to jasno napisane bez przeczytania całej specyfikacji podsystemu Współrzędna robocza z zarządzaniem współbieżnym (cmwq) i nawet wtedy niektóre informacje są nieaktualne!
Krótka odpowiedź
Will [kod] gwarantuje, że delayed_work będzie działać tak szybko, jak to możliwe?
Tak (z zastrzeżeniem poniżej)
Co się dzieje w sytuacji, gdy praca jest już uruchomiony?
Będzie on prowadzony w pewnym momencie po aktualnie uruchomione delayed_work
wyjść funkcyjnych i na tym samym CPU jako ostatni, chociaż każda inna praca już w kolejce na tym workqueue (lub opóźnionego pracy, który jest należny) będzie być pierwszym na mecie. Zakłada się, że nie zainicjowano ponownie obiektu delayed_work
lub work_struct
i że nie zmieniono wskaźnika wartości work->func
.
długa odpowiedź
Więc najpierw, struct delayed_work
używa pseudo-dziedziczenie czerpać z struct work_struct
przez osadzanie struct work_struct
jako jej pierwszy człon. Ten podsystem używa jakiegoś niesamowitego atomowego sprzężenia bitów, by mieć poważną współbieżność. A work_struct
jest "własnością", gdy ma ono ustawiony bit data
. Kiedy robotnik wykonuje twoją pracę, to releases ownership i rejestruje ostatnią pulę roboczą za pomocą prywatnej funkcji set_work_pool_and_clear_pending() - jest to ostatni czas, gdy API modyfikuje obiekt work_struct
(aż do czasu ponownego jego zaplanowania, oczywiście). Wywołanie cancel_delayed_work()
wykonuje dokładnie to samo.
Więc jeśli zadzwonisz pod numer cancel_delayed_work()
, gdy twoja funkcja robocza już się zaczęła, zwróci false
(zgodnie z reklamą), ponieważ nie jest już własnością nikogo, nawet jeśli nadal może być uruchomiona. Jednak przy próbie ponownego dodania go za pomocą schedule_delayed_work()
, będzie examine the work, aby odkryć ostatnie pool_workqueue
, a następnie dowiedzieć się, czy któryś z tych pracowników pool_workqueue
pracuje obecnie.Jeśli są (i nie zmieniłeś wskaźnika work->func
), to po prostu dołączają pracę do kolejki tego pool_workqueue
i to w ten sposób unika ponownego entrancy! W przeciwnym razie spowoduje umieszczenie go w kolejce do bieżącego procesora. (Powodem kontroli work->func
wskaźnik jest umożliwienie ponownego wykorzystania obiektu work_struct
.)
jednak pamiętać, że po prostu dzwoniąc schedule_delayed_work()
bez anulowania najpierw spowoduje nie zmianę czy praca jest nadal w kolejce, więc na pewno musi najpierw anulować.
EDYCJA: O tak, jeśli jesteś zdezorientowany przez dyskusję w Documentation/workqueue.txt
o WQ_NON_REENTRANT
, zignoruj to. Ta flaga jest uznawana za przestarzałą i ignorowana, a wszystkie paski robocze są teraz nieresetujące.
uugh! To jest mój obecny dylemat. W rzeczywistości jeden z zapisów śledzenia, które miałem w oops, wskazał, że mógł on ponownie wejść po wykonaniu twojego kodu powyżej. Dodałem licznik atomowy, aby temu zapobiec, ale muszę również znać poprawny sposób na zmianę terminu delayed_work. –