diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 01f72bae..60705aa3 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2538,6 +2538,7 @@ Markus Ehrnsperger for reporting a bug in error handling when loading a plugin for reporting a possible crash in cIndexFile::GetClosestIFrame() for reporting a missing 'const' in cTimers::GetTimerForEvent() + for suggesting to add a chapter about locking to PLUGINS.html Werner Färber for reporting a bug in handling the cPluginManager::Active() result when pressing diff --git a/HISTORY b/HISTORY index abfc26f8..b62a93dd 100644 --- a/HISTORY +++ b/HISTORY @@ -9800,3 +9800,4 @@ Video Disk Recorder Revision History - Fixed a possible deadlock in case two SVDRP clients send each other POLL commands at the same time. - Added a missing 'const' to cTimers::GetTimerForEvent() (reported by Markus Ehrnsperger). +- Added a chapter about locking to PLUGINS.html (suggested by Markus Ehrnsperger). diff --git a/PLUGINS.html b/PLUGINS.html index f5e0d291..deb5a313 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -83,6 +83,7 @@ structures and allows it to hook itself into specific areas to perform special a
  • Internationalization
  • Custom services
  • SVDRP commands +
  • Locking
  • Loading plugins into VDR
  • Building the distribution package @@ -1185,9 +1186,59 @@ when presenting them to the caller, and the continuation character ('-' will be set for all but the last one.

    The SVDRP functions are called from the separate "SVDRP server handler" thread. -Therefore the plugin needs to take care of proper locking if it accesses any +Therefore the plugin needs to take care of proper locking if it accesses any global data. +


    Locking

    + +
    U can't touch this

    + +When accessing global data structures, proper locking is absolutely necessary. +

    +There are several macros that allow for easy locking and unlocking. They all +follow the naming convention LOCK_*_READ|WRITE, where '*' is the name +of the global data structure, which can be one of TIMERS, CHANNELS, +RECORDINGS or SCHEDULES. To implicitly avoid deadlocks in case you +need to lock more than one structure, always make sure you use these macros in +this sequence! +

    +Using one of these macros sets a lock on the structure, creates a local pointer to the +respective structure and makes sure the lock is released at the end of the current +block: + +

    +if (some condition) {
    +   LOCK_TIMERS_READ; // creates local const cTimers *Timers
    +   for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
    +       // do something with Timer
    +       }
    +   }
    +

    + +Note the naming convention: TIMERS -> Timers etc. +

    +The LOCK_*_READ macros create pointers that are 'const', while +the LOCK_*_WRITE macros allow modifications to the data structures. +Both wait indefinitely to obtain the lock. However, if LOCK_*_WRITE is +used twice in the same block, the second call will not obtain a lock and +immediately return NULL, which may lead to a crash. In such cases a +warning and backtrace is logged. +

    +You may keep pointers to objects in such lists, even after releasing +the lock. However, you may only access such objects if you are +holding a proper lock again. If an object has been deleted from the list +while you did not hold a lock (for instance by an other thread), the +object will still be there, but no longer within this list (it is then +stored in the ListGarbageCollector for a few seconds). That way even if you +access the object after it has been deleted, you won't cause a segfault. +You can call the Contains() function to check whether an object you are +holding a pointer to is still in the list. Note that the garbage collector +is purged when the usual housekeeping is done. +

    +See tools.h, class cListBase for more documentation and information on how +to use locking with timeouts, and thread.h, classes cStateLock and +cStateKey on how to easily react to changes in such lists. +


    Loading plugins into VDR

    Saddling up!