aboutsummaryrefslogtreecommitdiffhomepage
path: root/common.h
diff options
context:
space:
mode:
authorGravatar Kevin Ballard <kevin@sb.org>2014-08-24 00:59:03 -0700
committerGravatar Kevin Ballard <kevin@sb.org>2014-08-29 12:46:03 -0700
commitcc52a59e1ac7155123a314067cc8ad9f3affe491 (patch)
tree26a24c179ad7f6e8bfe59e77a8f59b53520f7d07 /common.h
parent24ac7d2698c06792cc78a82bb06f41fcdeb0934c (diff)
Rework how screen size is tracked
The screen size is fetched after a SIGWINCH is delivered. The current implementation has two issues: * It calls ioctl() from the SIGWINCH signal handler, despite ioctl() not being a function that is known to be safe to call. * It's not thread-safe. Signals can be delivered on arbitrary threads, so we don't know if it's actually safe to be modifying the cached winsize in response to a signal. It's also plausible that the winsize may be requested from a background thread. To solve the first issue, we twiddle a volatile boolean flag in the signal handler and defer the ioctl() call until we actually request the screen size. To solve the second issue, we introduce a pthread rwlock around the cached winsize. A rwlock is used because it can be expected that there are likely to be far more window size reads than window size writes. If we were using C++11 we could probably get away with atomics, but since we don't have that (or boost), a rwlock should suffice. Fixes #1613.
Diffstat (limited to 'common.h')
-rw-r--r--common.h50
1 files changed, 47 insertions, 3 deletions
diff --git a/common.h b/common.h
index ff12760d..84c908aa 100644
--- a/common.h
+++ b/common.h
@@ -121,7 +121,9 @@ inline bool selection_direction_is_cardinal(selection_direction_t dir)
/**
Helper macro for errors
*/
-#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { int err = errno; fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", #a, __LINE__, __FILE__, err, strerror(err)); abort(); }} while (0)
+#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { VOMIT_ABORT(errno, #a); } } while (0)
+#define VOMIT_ON_FAILURE_NO_ERRNO(a) do { int err = (a); if (0 != err) { VOMIT_ABORT(err, #a); } } while (0)
+#define VOMIT_ABORT(err, str) do { int code = (err); fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", str, __LINE__, __FILE__, code, strerror(code)); abort(); } while(0)
/** Exits without invoking destructors (via _exit), useful for code after fork. */
void exit_without_destructors(int code) __attribute__((noreturn));
@@ -544,12 +546,12 @@ class mutex_lock_t
pthread_mutex_t mutex;
mutex_lock_t()
{
- pthread_mutex_init(&mutex, NULL);
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_init(&mutex, NULL));
}
~mutex_lock_t()
{
- pthread_mutex_destroy(&mutex);
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_destroy(&mutex));
}
};
@@ -571,6 +573,48 @@ public:
~scoped_lock();
};
+class rwlock_t
+{
+public:
+ pthread_rwlock_t rwlock;
+ rwlock_t()
+ {
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_init(&rwlock, NULL));
+ }
+
+ ~rwlock_t()
+ {
+ VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_destroy(&rwlock));
+ }
+};
+
+/*
+ Scoped lock class for rwlocks
+ */
+class scoped_rwlock
+{
+ pthread_rwlock_t *rwlock_obj;
+ bool locked;
+ bool locked_shared;
+
+ /* No copying */
+ scoped_rwlock &operator=(const scoped_lock &);
+ scoped_rwlock(const scoped_lock &);
+
+public:
+ void lock(void);
+ void unlock(void);
+ void lock_shared(void);
+ void unlock_shared(void);
+ /*
+ upgrade shared lock to exclusive.
+ equivalent to `lock.unlock_shared(); lock.lock();`
+ */
+ void upgrade(void);
+ scoped_rwlock(pthread_rwlock_t &rwlock, bool shared = false);
+ scoped_rwlock(rwlock_t &rwlock, bool shared = false);
+ ~scoped_rwlock();
+};
/**
A scoped manager to save the current value of some variable, and optionally