void Take() {
while (pthread_mutex_lock(&mutex_)) {
}
+ holder_ = pthread_self();
+ isBusy_ = true;
+ }
+ bool TakeIfNoDeadlock() {
+ if (isBusy_) {
+ auto thisThread{pthread_self()};
+ if (pthread_equal(thisThread, holder_)) {
+ return false;
+ }
+ }
+ Take();
+ return true;
}
bool Try() { return pthread_mutex_trylock(&mutex_) == 0; }
- void Drop() { pthread_mutex_unlock(&mutex_); }
+ void Drop() {
+ isBusy_ = false;
+ pthread_mutex_unlock(&mutex_);
+ }
#elif defined(_WIN32)
Lock() { InitializeCriticalSection(&cs_); }
~Lock() { DeleteCriticalSection(&cs_); }
private:
#if USE_PTHREADS
pthread_mutex_t mutex_{};
+ volatile bool isBusy_{false};
+ volatile pthread_t holder_;
#elif defined(_WIN32)
CRITICAL_SECTION cs_;
#else
void ExternalFileUnit::EndIoStatement() {
io_.reset();
u_.emplace<std::monostate>();
- CriticalSection critical{lock_};
- isBusy_ = false;
+ lock_.Drop();
}
void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
template <typename A, typename... X>
IoStatementState &BeginIoStatement(const Terminator &terminator, X &&...xs) {
- bool alreadyBusy{false};
- {
- CriticalSection critical{lock_};
- alreadyBusy = isBusy_;
- isBusy_ = true; // cleared in EndIoStatement()
- }
- if (alreadyBusy) {
- terminator.Crash("Could not acquire exclusive lock on unit %d, perhaps "
- "due to an attempt to perform recursive I/O",
- unitNumber_);
+ // Take lock_ and hold it until EndIoStatement().
+#if USE_PTHREADS
+ if (!lock_.TakeIfNoDeadlock()) {
+ terminator.Crash("Recursive I/O attempted on unit %d", unitNumber_);
}
+#else
+ lock_.Take();
+#endif
A &state{u_.emplace<A>(std::forward<X>(xs)...)};
if constexpr (!std::is_same_v<A, OpenStatementState>) {
state.mutableModes() = ConnectionState::modes;
std::int32_t ReadHeaderOrFooter(std::int64_t frameOffset);
Lock lock_;
- // TODO: replace with a thread ID
- bool isBusy_{false}; // under lock_
int unitNumber_{-1};
Direction direction_{Direction::Output};