diff options
author | 2014-08-24 00:59:03 -0700 | |
---|---|---|
committer | 2014-08-29 12:46:03 -0700 | |
commit | cc52a59e1ac7155123a314067cc8ad9f3affe491 (patch) | |
tree | 26a24c179ad7f6e8bfe59e77a8f59b53520f7d07 /common.h | |
parent | 24ac7d2698c06792cc78a82bb06f41fcdeb0934c (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.h | 50 |
1 files changed, 47 insertions, 3 deletions
@@ -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 |