Today, I tested rtcwake on several x86 or x86-64 based computers.
The outcome:
(1) Suspend to RAM (say, "rtcwake -m mem -s 10"):
* Success: Every system.
(2) Wake-on-timer ("rtcwake -m no -s 120 && shutdown -h now" or "rtcwake
-m off -s 120"):
* Success: Lenovo Thinkpad X220 (2012?), and a 5-year-old desktop system
* Fail: IBM Thinkpad X60 (Core Duo from 2006), a HP Zbook from 2016.
(3) Suspend to disk ("rtcwake -m disk -s 120") had no chance of working,
because I never configure …
[View More]any swap partition. I would assume that this
could only work if (2) worked in the first place. On the Debian systems
that I tried, the command would fail as expected. On one Arch Linux
system (Lenovo Thinkpad X220), it forcibly power off the computer and
fail to start automatically; a file system check ran on the manual
power-on.
GNU/Linux is the only operating system on each computer, and apart from
the 32-bit Thinkpad X60, everything boots up via UEFI.
In the BIOS setup of both the Thinkpad X60 and the HP laptop there are
some settings related to powering up on timer. Maybe with something like
nvram-wakeup (which was the only working option for a 2001 Intel Celeron
machine) they could be made to work.
Moral of the story: You can't expect wake-on-timer to "just work" even
on relatively recent hardware. Possibly the chances are better with
desktop or small-form-factor systems than with laptops.
Marko
[View Less]
Some time ago, I created the wiki page
https://www.linuxtv.org/vdrwiki/index.php/Systemd that describes much of
my VDR installation.
On my Raspberry Pi, there is no real-time-clock. My low-tech solution
for waking up VDR for recordings is that I set an alarm on my phone, to
remind me to turn on VDR when needed. Today, I refined that a little by
implementing an idle timeout shutdown when no further recording timers
exist, or when they are in the distant enough future.
I learned that …
[View More]there is a portable Linux tool "rtcwake" that should
support whatever is needed by VDR. It is much simpler than the
"nvram-wakeup" that I used almost 20 years ago. On my Raspberry Pi,
basically any "rtcwake" commands will fail. That provided a good way of
testing the fallback mechanisms of my shutdown script.
I think that it could be useful to include some Systemd integration
scripts in the VDR distribution. My systemd integration consists of 4
parts that are documented in the
https://www.linuxtv.org/vdrwiki/index.php/Systemd wiki page:
* /etc/systemd/system/vdr-keep-alive.sh to control system shutdown
* /var/lib/vdr/vdr-shutdown.sh to handle VDR shutdown (vdr -s)
* some configuration to have a VDR service in Systemd
* optional: additional configuration to have the video directory on USB
storage, to have VDR auto-start when the storage is plugged in.
Because rtcwake does not work on my VDR system, it would be nice to get
some feedback from users of wake-on-timer capable systems. It is
possible that the "rtcwake -m disk -s $2" and "rtcwake -m mem -s $2"
require that "vdr-keep-alive.sh stop" be invoked first and
"vdr-keep-alive.sh start" be invoked once rtcwake successfully returns
(at the wake-up time). It is also possible that the VDR process would
need to be restarted.
Best regards,
Marko
[View Less]
Hi all,
I'm trying to consolidate my stuff a bit and got a 4Sat these days. I'm trying
to run VDR-2.6.0 with the satip plugin connecting to the Satip device. 4
receivers, 2x S19.2E, 2x S13.0E. So far, the LNB ran on a DD Cine PCI-E card
just fine. (but I want to get rid of the server). (Tuner #4 is a Fritzbox
cable receiver).
Initially, all seems to look good:
Mar 29 15:20:55 seneca vdr: [4921] initializing plugin: satip (2.4.1): SAT>IP Geräte
Mar 29 15:20:55 seneca vdr: [4924] SATIP …
[View More]poller thread started (pid=4921, tid=4924, prio=high)
Mar 29 15:20:55 seneca vdr: [4921] cTimeMs: using monotonic clock (resolution is 1 ns)
Mar 29 15:20:55 seneca vdr: [4921] new device number 1 (card index 1)
Mar 29 15:20:55 seneca vdr: [4925] SATIP discover thread started (pid=4921, tid=4925, prio=high)
Mar 29 15:20:55 seneca vdr: [4921] SATIP: Creating device CardIndex=0 DeviceNumber=0 [device 0]
Mar 29 15:20:55 seneca vdr: [4927] SATIP#0 section handler thread started (pid=4921, tid=4927, prio=high)
Mar 29 15:20:55 seneca vdr: [4921] new device number 2 (card index 2)
Mar 29 15:20:55 seneca vdr: [4928] device 1 section handler thread started (pid=4921, tid=4928, prio=low)
Mar 29 15:20:55 seneca vdr: [4921] SATIP: Creating device CardIndex=1 DeviceNumber=1 [device 1]
Mar 29 15:20:55 seneca vdr: [4929] SATIP#1 tuner thread started (pid=4921, tid=4929, prio=high)
Mar 29 15:20:55 seneca vdr: [4930] SATIP#1 section handler thread started (pid=4921, tid=4930, prio=high)
Mar 29 15:20:55 seneca vdr: [4926] SATIP#0 tuner thread started (pid=4921, tid=4926, prio=high)
Mar 29 15:20:55 seneca vdr: [4931] device 2 section handler thread started (pid=4921, tid=4931, prio=low)
Mar 29 15:20:55 seneca vdr: [4921] new device number 3 (card index 3)
Mar 29 15:20:55 seneca vdr: [4921] SATIP: Creating device CardIndex=2 DeviceNumber=2 [device 2]
Mar 29 15:20:55 seneca vdr: [4932] SATIP#2 tuner thread started (pid=4921, tid=4932, prio=high)
Mar 29 15:20:55 seneca vdr: [4921] new device number 4 (card index 4)
Mar 29 15:20:55 seneca vdr: [4934] device 3 section handler thread started (pid=4921, tid=4934, prio=low)
Mar 29 15:20:55 seneca vdr: [4921] SATIP: Creating device CardIndex=3 DeviceNumber=3 [device 3]
Mar 29 15:20:55 seneca vdr: [4935] SATIP#3 tuner thread started (pid=4921, tid=4935, prio=high)
Mar 29 15:20:55 seneca vdr: [4936] SATIP#3 section handler thread started (pid=4921, tid=4936, prio=high)
Mar 29 15:20:55 seneca vdr: [4937] device 4 section handler thread started (pid=4921, tid=4937, prio=low)
Mar 29 15:20:55 seneca vdr: [4921] new device number 5 (card index 5)
Mar 29 15:20:55 seneca vdr: [4921] SATIP: Creating device CardIndex=4 DeviceNumber=4 [device 4]
Mar 29 15:20:55 seneca vdr: [4938] SATIP#4 tuner thread started (pid=4921, tid=4938, prio=high)
Mar 29 15:20:55 seneca vdr: [4939] SATIP#4 section handler thread started (pid=4921, tid=4939, prio=high)
...
Mar 29 15:20:56 seneca vdr: [4921] switching to channel 1 S19.2E-1-1107-17503 (WELT)
Mar 29 15:20:58 seneca vdr: [4957] device 1 receiver thread started (pid=4921, tid=4957, prio=high)
...
Mar 29 15:21:01 seneca vdr: [4928] creating new channel 'SAT.1,;ProSiebenSat.1' on S19.2E transponder 112544 with id 1-1107-17500-0
Mar 29 15:21:01 seneca vdr: [4928] creating new channel 'ProSieben,;ProSiebenSat.1' on S19.2E transponder 112544 with id 1-1107-17501-0
Mar 29 15:21:01 seneca vdr: [4928] creating new channel 'kabel eins,;ProSiebenSat.1' on S19.2E transponder 112544 with id 1-1107-17502-0
Mar 29 15:21:01 seneca vdr: [4928] creating new channel 'SAT.1 Gold,;ProSiebenSat.1' on S19.2E transponder 112544 with id 1-1107-17504-0
... (plus a handful more)
but a minute later,
Mar 29 15:21:59 seneca vdr: [4929] curl_easy_perform() [rtsp.c,369] failed: Timeout was reached (28)
Mar 29 15:21:59 seneca vdr: [4929] SATIP-ERROR: Detected invalid status code 0: rtsp://192.168.20.7/ [device 1]
Mar 29 15:21:59 seneca vdr: [4929] SATIP-ERROR: Pid update failed - retuning [device 1]
Mar 29 15:21:59 seneca vdr: [4932] curl_easy_perform() [rtsp.c,340] failed: Timeout was reached (28)
Mar 29 15:21:59 seneca vdr: [4932] SATIP-ERROR: Detected invalid status code 0: rtsp://192.168.20.7/ [device 2]
Mar 29 15:21:59 seneca vdr: [4935] curl_easy_perform() [rtsp.c,244] failed: Timeout was reached (28)
Mar 29 15:21:59 seneca vdr: [4935] SATIP-ERROR: Detected invalid status code 0: rtsp://192.168.20.7/ [device 3]
Mar 29 15:21:59 seneca vdr: [4935] SATIP-ERROR: Connect failed [device 3]
... and
Mar 29 15:22:03 seneca vdr: [4935] SATIP-ERROR: Detected invalid status code 503: rtsp://192.168.20.7/ [device 3]
Mar 29 15:22:03 seneca vdr: [4935] SATIP-ERROR: Connect failed [device 3]
Mar 29 15:22:04 seneca vdr: [4929] SATIP-ERROR: Detected invalid status code 503: rtsp://192.168.20.7/ [device 1]
Mar 29 15:22:04 seneca vdr: [4929] SATIP-ERROR: Connect failed [device 1]
etc. etc. endlessly, and with netstat I see a lot of attempted rtsp connects:
tcp 0 0 192.168.20.1:42764 192.168.20.7:554 ESTABLISHED 5219/vdr
tcp 0 0 192.168.20.1:53584 192.168.20.7:554 TIME_WAIT -
What might be wrong? Any experience that you can share? Should I return this
device because it is known not to work with the satip plugin? (Not that I
didn't google this before, to no avail.)
I would have preferred an Octopus Net S2, but these devices were withdrawn and
are hard to find.
TIA!
Cheers,
hm
--
We should be careful to get out of an experience only the wisdom that is
in it - and stay there, lest we be like the cat that sits down on a hot
stove-lid. She will never sit down on a hot stove-lid again - and that
is well; but also she will never sit down on a cold one any more.
-- Mark Twain
[View Less]
VDR version 2.6.4 is now available at the official VDR GIT archive
git://git.tvdr.de
You can also get the latest stable version with
git clone --branch stable/2.6 git://git.tvdr.de/vdr.git
or as a tar archive with
http://git.tvdr.de/?p=vdr.git;a=snapshot;h=refs/tags/2.6.4;sf=tbz2
This version fixes a few bugs that came up after the release of version 2.6.3.
The changes since version 2.6.3:
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- Fixed restoring …
[View More]the volume at program start (thanks to Matthias Senzel).
- Fixed symmetry of Begin/EndSegmentTransfer() calls in cEIT::cEIT() (thanks to
Jörg Wendel).
- Added a note to epg.h about not messing with event ids (suggested by Winfried Köhler).
- Added a note to vdr.5 about event ids possibly changing when an event moves from
one table to another.
- Fixed initializing cDvbPlayerControl (was broken in version 2.6.3).
- Reverted 'Fixed a possible crash if an editing process is canceled while the edited
recording is being replayed' (introduced in version 2.6.2), because it caused a
deadlock when moving recordings between volumes.
- Fixed a possible crash if an editing process is canceled while the edited recording
is being replayed (new solution).
- Fixed unnecessary interruption of ongoing recordings if timers avoided the transfer
mode receiver device (thanks to Markus Ehrnsperger).
- Revised support for kernel based LIRC driver (thanks to Marko Mäkelä).
Homepage: http://www.tvdr.de
Facebook: https://www.facebook.com/VideoDiskRecorder
Have fun!
Klaus
[View Less]
Hi,
I would propose the following patch, or some equivalent interface that
would allow cThread::mutex to be used with some cCondVar in derived
classes:
diff --git a/thread.h b/thread.h
index 16c4bd75..cd1d98ab 100644
--- a/thread.h
+++ b/thread.h
@@ -83,7 +83,9 @@ private:
bool running;
pthread_t childTid;
tThreadId childThreadId;
+protected:
cMutex mutex;
+private:
char *description;
bool lowPriority;
static tThreadId mainThreadId;
Because cThread::mutx is …
[View More]declared private and there is no helper member
function, derived classes that wish to use condition variables have to
instantiate a separate cMutex for that. Here is an example from the
rpihddevice plugin that illustrates my point:
diff --git a/omx.c b/omx.c
--- a/omx.c
+++ b/omx.c
@@ -119,17 +119,17 @@ const char* cOmx::errStr(int err)
void cOmx::Action(void)
{
cTimeMs timer;
- m_mutex.Lock();
+ Lock();
while (Running())
{
while (m_portEvents.empty())
- if (m_portEventsAdded.TimedWait(m_mutex, 10))
+ if (!m_portEventsAdded.TimedWait(mutex, 10))
goto timeout;
{
const Event event = m_portEvents.front();
m_portEvents.pop();
- m_mutex.Unlock();
+ Unlock();
switch (event.event)
{
Actually, there is a bug above: the condition for the TimedWait() call
was inverted.
This code illustrates another limitation: There is no way to pass an
absolute time to cCondVar::TimedWait(). On each call, a relative wake-up
time (milliseconds from the current time) will be converted into an
absolute time. If there was a way, we would be able to remove the
"cTimeMs timer" and some related system calls, and have this loop both
wake up every 100 milliseconds, and process events as soon as they
arrive. Here is the VDR part of the patch:
diff --git a/thread.c b/thread.c
index 93eb8c0d..3dcb44d4 100644
--- a/thread.c
+++ b/thread.c
@@ -36,7 +36,7 @@
#define dbglocking(a...)
#endif
-static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+bool cCondVar::GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
{
struct timeval now;
if (gettimeofday(&now, NULL) == 0) { // get current time
@@ -81,7 +81,7 @@ bool cCondWait::Wait(int TimeoutMs)
if (!signaled) {
if (TimeoutMs) {
struct timespec abstime;
- if (GetAbsTime(&abstime, TimeoutMs)) {
+ if (cCondVar::GetAbsTime(&abstime, TimeoutMs)) {
while (!signaled) {
if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
break;
@@ -129,20 +129,27 @@ void cCondVar::Wait(cMutex &Mutex)
}
}
+bool cCondVar::TimedWait(cMutex &Mutex, const timespec &abstime)
+{
+ int err = 0;
+
+ if (int locked = Mutex.locked) {
+ Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
+ // does an implicit unlock of the mutex.
+ err = pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime);
+ Mutex.locked = locked;
+ }
+ return err != ETIMEDOUT;
+}
+
bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
{
bool r = true; // true = condition signaled, false = timeout
if (Mutex.locked) {
struct timespec abstime;
- if (GetAbsTime(&abstime, TimeoutMs)) {
- int locked = Mutex.locked;
- Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
- // does an implicit unlock of the mutex.
- if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
- r = false;
- Mutex.locked = locked;
- }
+ if (GetAbsTime(&abstime, TimeoutMs))
+ r = TimedWait(Mutex, abstime);
}
return r;
}
@@ -174,7 +181,7 @@ bool cRwLock::Lock(bool Write, int TimeoutMs)
int Result = 0;
struct timespec abstime;
if (TimeoutMs) {
- if (!GetAbsTime(&abstime, TimeoutMs))
+ if (!cCondVar::GetAbsTime(&abstime, TimeoutMs))
TimeoutMs = 0;
}
if (Write) {
diff --git a/thread.h b/thread.h
index 16c4bd75..04bb4cc5 100644
--- a/thread.h
+++ b/thread.h
@@ -49,7 +49,9 @@ public:
~cCondVar();
void Wait(cMutex &Mutex);
bool TimedWait(cMutex &Mutex, int TimeoutMs);
+ bool TimedWait(cMutex &Mutex, const timespec &abstime);
void Broadcast(void);
+ static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow);
};
class cRwLock {
I did not complete the change to rpihddevice cOmx::Action() yet. We may
be forced to retain two mutexes after all, because OMX_EmptyThisBuffer()
as well as one of the functions called from cOmx::StopAudio() would hang
indefinitely, while holding the cOmx::mutex, blocking the cOmx::Action()
thread. According to
https://forums.raspberrypi.com/viewtopic.php?t=313170 the interface is
largely deprecated. I will try to figure out something.
Best regards,
Marko
[View Less]
Yesterday, I finally bought external storage for my Raspberry Pi based
VDR setup, a Samsung Portable SSD T7. It supports USB 3, but it also
works on the Raspberry Pi 2's USB 2.0 and does not consume too much
power. My old tower PC case based system that I had set up in 2004 has
now been replaced with something that is better in every thinkable
respect: power consumption, noise (passive cooling, no HDDs), speed, and
size (not much larger than the remote control unit).
Hardware:
* …
[View More]Raspberry Pi 2
* Pi TV hat
* TV hat case
* an IR receiver attached via soldered wires to the TV hat, at GPIO pin 18
* a remote control unit (from an old Hauppauge Nova-T PCI card)
* Samsung Portable SSD T7 (1 TB)
Software:
* Raspberry OS Legacy installed on a MicroSD card, with no GUI
* sudo apt install ir-keytable
* VDR 2.6.3 (or 2.6.2) compiled from source
* https://github.com/reufer/rpihddevice/ compiled from source
* "make install" to /usr/local
My /boot/config.txt includes the following lines:
dtoverlay=gpio-ir,gpio_pin=18
dtparam=audio=on
gpu_mem=256
In /etc/rc_maps.cfg (the configuration file of ir-keytable), ensure that
there is a line like the following that will match the remote control
unit that you are using:
* * hauppauge.toml
The above works for several RC5 based Hauppauge remote control units.
To prevent the Power button on the remote control unit from shutting
down the entire system, add the following to the [Login] section of
/etc/systemd/logind.conf:
HandlePowerKey=ignore
The default is HandlePowerKey=poweroff.
Use mkfs.ext4 to replace the FAT file system of the only partition of
the T7. There is no need to change the partitioning or specify a block
size or alignment, because the physical block size is reported as 512
bytes. Optionally, you may set a label by executing something like this:
tune2fs -L VDR /dev/sda1
You may create a mount point:
sudo mkdir -m 000 /video
Then, add a line like this to /etc/fstab to have the storage mounted
automatically:
LABEL=VDR /video ext4 defaults,noatime,nofail 0 1
You may replace the LABEL=VDR with whatever symbolic link you have in
/dev/disk/by-label (see also tune2fs above). On my system, I actually
wrote PARTUUID=33d32895-01 because there is a symbolic link
/dev/disk/by-partuuid/33d32895-01 that identifies the partition.
Once the storage is mounted, execute the following:
sudo mkdir /video/video
sudo chown pi:pi /video/video
The next step is to configure VDR to start up correctly. I have some
configuration files in /var/lib/vdr. For testing, I used to start VDR
manually from the command line, and shut it down by choosing "restart"
from the OSD menu. Now I want it to restart automatically, but only if
suitable USB storage has been plugged in:
sudo tee /etc/systemd/system/vdr.service << EOF
[Unit]
After=systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service
After=getty(a)tty1.service
After=video.mount
Conflicts=getty(a)tty1.service
ConditionPathExists=/video/video
[Service]
User=pi
ExecStart=/usr/local/bin/vdr --no-kbd --lirc=/dev/lirc0 -Prpihddevice -v /video/video -s /var/lib/vdr/vdr-shutdown.sh
TimeoutStartSec=infinity
Type=idle
Restart=on-failure
RestartSec=1s
TTYVTDisallocate=yes
[Install]
Alias=display-manager.service
EOF
This will replace the getty process on virtual terminal 1 (tty1). If the
storage is not plugged within 90 seconds from startup (I do not know how
to configure that timeout), then an error message will appear on the
console. No getty will be started on tty1 in any case; you can always
log in from tty2 by pressing Alt+F2.
The shutdown script /var/lib/vdr/vdr-shutdown.sh does not work as
intended yet:
#!/bin/sh
if [ "$5" = 1 ]
then
sudo service vdr stop
sudo umount /video
sudo udisksctl power-off -b /dev/sda
fi
The first step appears to terminate the shell script, because the shell
is a subprocess of VDR. So, the storage will remain mounted and powered
on. I guess that we need to launch a separate "vdr-shutdown" service
that would take care of the remaining steps. Has someone already
implemented something like this?
After the "umount" and "udisksctl" commands are executed, it is safe to
unplug the storage. The LED of the SSD will shortly change color and
then turn off during the execution of the "udisksctl" command.
What I am also missing is a udev rule that would automatically mount the
storage and attempt to start up VDR as soon as the storage is plugged
in. Currently, I have to manually execute the following if I plug in the
drive to an already running system:
sudo mount /video
sudo service vdr start
This configuration provides a rather simple user interface for VDR. No
keyboard or mouse is needed, just the remote control unit, a display,
and optionally the USB cable, if the system has other uses that are
independent of VDR.
For timed recordings, I think that on the Raspberry Pi, it is easiest to
let the VDR process run all the time. Starting up the Pi based on timer
would require additional hardware.
One thing that I'd like to improve in that regard is to let VDR shut
down all tuners when the system is idle. This could be based on an
inactivity timer or an explicit user action, such as pressing a button
on the remote control. The video output would shut off, and the tuner
would be powered off, except when needed for something (EPG scan,
recording). As soon as a button is pressed on the remote control unit,
the user interface would spring back to life.
Happy holidays,
Marko
[View Less]
VDR version 2.6.3 is now available at the official VDR GIT archive
git://git.tvdr.de
You can also get the latest stable version with
git clone --branch stable/2.6 git://git.tvdr.de/vdr.git
or as a tar archive with
http://git.tvdr.de/?p=vdr.git;a=snapshot;h=refs/tags/2.6.3;sf=tbz2
This version fixes a few bugs that came up after the release of version 2.6.2.
The changes since version 2.6.2:
- Fixed a compiler warning.
- Fixed generating the index file in the cutter (…
[View More]reported by Christoph Haubrich).
- Fixed a faulty 'Timer still recording' query when canceling an editing job.
- Added code for the 'qks' audio track (thanks to Johann Friedrichs).
- Fixed a possible heap-use-after-free in cDvbTuner::Action() (reported by Marko Mäkelä).
- Fixed initializing cDvbPlayerControl and cTransferControl (reported by Marko Mäkelä).
- Now avoiding calling poll() in cSectionHandler::Action() if there are no filters
(reported by Marko Mäkelä).
- Now avoiding the memcpy() call in cGlyph::cGlyph() if the bitmap is empty (thanks
to Marko Mäkelä).
- Now avoiding unnecessary processing in cDvbSubtitleConverter::FinishPage() if there
are no areas (thanks to Marko Mäkelä).
- Avoiding a zero sized array in cDevice::GetDevice() (thanks to Marko Mäkelä).
- Now checking the video directory after setting the user id.
Homepage: http://www.tvdr.de
Facebook: https://www.facebook.com/VideoDiskRecorder
Have fun!
Klaus
[View Less]
Because of the heap-use-after-free race condition that was rather easily
reproducible with AddressSanitizer (-fsanitize=address), I thought that
I should finally try to learn to use ThreadSanitizer (TSAN,
-fsanitize=thread in GCC and clang).
https://clang.llvm.org/docs/ThreadSanitizer.html
Because VDR makes use of POSIX thread synchronization primitives, no
additional instrumentation via <sanitizer/tsan_interface.h> should be
necessary.
Before C++11 defined a memory model for …
[View More]multi-threaded applications,
semantics around shared data structures were rather unclear, and I would
guess that most multi-threaded pre-C++11 code bases would trip
ThreadSanitizer. Also, multi-threaded CPUs were rare in the early days,
and the Total Store Order of the x86 is very forgiving, compared to the
weak memory model of ARM (see
https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html for some
examples).
To silence one prominent source of TSAN warnings in the cThread
destructor, I applied the attached patch. It is not ideal, because
std::atomic defaults to std::memory_order_seq_cst while
std::memory_order_relaxed would likely suffice here.
Even after applying the first patch, a simple test with no DVB receiver
device and no valid data directory would still produce a couple of data
race reports (see the end of this message). I recorded a trace of such a
run with "rr record" (https://rr-project.org) and debugged it in "rr
replay". Unsurprisingly, I did not find actual evidence of a race
condition.
Finally, I figured out what is causing the first report:
cThread::description is not protected by cThread::mutex. Possibly, most
cThread data members (including cThread::active) should be protected by
cThread::mutex?
With both attached patches applied, the report of the first race will
disappear. The second race is apparently about some memory that is
allocated inside opendir(). I did not figure it out yet.
Related to this, cThread::Cancel() especially when invoked with
WaitSeconds=-1 looks problematic to me, and I see that VDR is invoking
pthread_detach() and never invoking pthread_join(). The second patch
includes an attempt to clean this up as well.
Both patches are just a proof of concept; I did not test them beyond the
simple failing-to-start VDR run under TSAN. Unfortunately, TSAN is not
available for my primary VDR hardware, running on 32-bit ARM.
With best regards,
Marko
vdr: error while reading '/var/lib/vdr/sources.conf'
vdr: error while reading '/var/lib/vdr/channels.conf'
vdr: no primary device found - using first device!
==================
WARNING: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call) (pid=96847)
Write of size 8 at 0x7ffc7f773e60 by main thread:
#0 cThread::~cThread() /dev/shm/vdr/thread.c:249 (vdr+0x1d72b8)
#1 cEpgDataReader::~cEpgDataReader() /dev/shm/vdr/epg.h:236 (vdr+0xa956b)
#2 main /dev/shm/vdr/vdr.c:731 (vdr+0xa956b)
Previous read of size 8 at 0x7ffc7f773e60 by thread T2:
#0 cThread::StartThread(cThread*) /dev/shm/vdr/thread.c:293 (vdr+0x1d76d9)
Location is stack of main thread.
Location is global '<null>' at 0x000000000000 ([stack]+0x1fe60)
Thread T2 'epg data reader' (tid=96855, finished) created by main thread at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1001 (libtsan.so.2+0x5e686)
#1 cThread::Start() /dev/shm/vdr/thread.c:316 (vdr+0x1d6fe0)
#2 main /dev/shm/vdr/vdr.c:804 (vdr+0xaa477)
SUMMARY: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call) /dev/shm/vdr/thread.c:249 in cThread::~cThread()
==================
==================
WARNING: ThreadSanitizer: data race (pid=96847)
Write of size 8 at 0x7b04000005a0 by main thread:
#0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:706 (libtsan.so.2+0x47e82)
#1 cString::~cString() /dev/shm/vdr/tools.c:1097 (vdr+0x1e52df)
#2 cxa_at_exit_wrapper ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:389 (libtsan.so.2+0x2dee3)
Previous read of size 8 at 0x7b04000005a0 by thread T1:
#0 opendir ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:3271 (libtsan.so.2+0x4c641)
#1 cReadDir::cReadDir(char const*) /dev/shm/vdr/tools.c:1553 (vdr+0x1ea8bd)
#2 cVideoDirectoryScannerThread::ScanVideoDir(char const*, int, int) /dev/shm/vdr/recording.c:1439 (vdr+0x180620)
#3 cVideoDirectoryScannerThread::Action() /dev/shm/vdr/recording.c:1433 (vdr+0x180bfc)
#4 cThread::StartThread(cThread*) /dev/shm/vdr/thread.c:293 (vdr+0x1d76ea)
Thread T1 'video directory' (tid=96854, running) created by main thread at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1001 (libtsan.so.2+0x5e686)
#1 cThread::Start() /dev/shm/vdr/thread.c:316 (vdr+0x1d6fe0)
#2 cRecordings::Update(bool) /dev/shm/vdr/recording.c:1554 (vdr+0x175387)
#3 main /dev/shm/vdr/vdr.c:788 (vdr+0xaa3f8)
SUMMARY: ThreadSanitizer: data race /dev/shm/vdr/tools.c:1097 in cString::~cString()
==================
ThreadSanitizer: reported 2 warnings
[View Less]