Skip to content

Commit b8b93cb

Browse files
committed
Bugfix: Do not block in record processing (fixes #5).
With #4, a bug was introduced that causes processing of the bo record triggering the execution of the command to block until the execution of the command complete, when the no-wait flag is set. The cause was that Command::run created a future using std::async, but this future was destroyed when the function returned. However, the destructor of std::future may block when the future has been created by std::async and the shared state is not available yet. This is fixed now by keeping the future and only destroying it on the next call to Command::run, when the shared state is available.
1 parent a952708 commit b8b93cb

2 files changed

Lines changed: 30 additions & 4 deletions

File tree

executeApp/src/Command.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
#include <algorithm>
3131
#include <cassert>
3232
#include <cstring>
33-
#include <future>
3433
#include <stdexcept>
3534
#include <system_error>
3635
#include <utility>
@@ -628,6 +627,15 @@ void Command::run() {
628627
stdinBuffer = this->stdinBuffer;
629628
stderrCapacity = this->stderrCapacity;
630629
stdoutCapacity = this->stdoutCapacity;
630+
// We want to clean up futures that have been created by earlier calls to
631+
// this method and that are ready now. We cannot remove futures that are not
632+
// ready yet, because this operation might block.
633+
pendingFutures.remove_if([](std::future<void> const &future) {
634+
return (
635+
!future.valid()
636+
|| (future.wait_for(std::chrono::seconds::zero())
637+
!= std::future_status::timeout));
638+
});
631639
}
632640
// The pointers stored in the following two vectors are only valid as long as
633641
// the original vectors exist and have not been changed. This is okay, because
@@ -790,7 +798,22 @@ void Command::run() {
790798
// process to terminate, we have to do this in the spawned thread.
791799
// Otherwise, the child process would never be reaped after terminating
792800
// and stay as a zombie process until the whole IOC terminates.
793-
stdinPipe.writeDataAsyncAndWaitForPid(childPid);
801+
auto future = stdinPipe.writeDataAsyncAndWaitForPid(childPid);
802+
// The destructor of std::future may block when the future has been
803+
// created by std::async, the shared state is not ready yet, and the
804+
// future represents the last reference to the shared state.
805+
// In our case, the first and third criteria is always fulfilled, which
806+
// means that the destructor may block (and in fact does block on at least
807+
// two platforms that were tested) when the command has not finished
808+
// running yet, which is exactly what we are trying to avoid when the wait
809+
// flag is not set.
810+
// For this reason, we append the future to a list, from which it will be
811+
// removed when the run() method is called again and the shared state of
812+
// the future is ready.
813+
{
814+
std::lock_guard<std::mutex> lock(mutex);
815+
pendingFutures.push_front(std::move(future));
816+
}
794817
}
795818
}
796819
}

executeApp/src/Command.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright 2018 aquenos GmbH.
3-
* Copyright 2018 Karlsruhe Institute of Technology.
2+
* Copyright 2018-2022 aquenos GmbH.
3+
* Copyright 2018-2022 Karlsruhe Institute of Technology.
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU Lesser General Public License as
@@ -31,6 +31,8 @@
3131
#define EPICS_EXEC_COMMAND_H
3232

3333
#include <cstdint>
34+
#include <forward_list>
35+
#include <future>
3436
#include <map>
3537
#include <mutex>
3638
#include <string>
@@ -183,6 +185,7 @@ class Command {
183185
std::map<int, std::string> arguments;
184186
std::map<std::string, std::string> envVars;
185187
mutable std::mutex mutex;
188+
std::forward_list<std::future<void>> pendingFutures;
186189
bool running;
187190
std::vector<char> stderrBuffer;
188191
std::size_t stderrCapacity;

0 commit comments

Comments
 (0)