0

I am having a C code that uses blocking I/O calls to file-descriptors some of which are Disk FDs so I am unable to use select(), but some I/O calls (read() / write()) are getting blocked for few seconds.

Is it possible to abort the system call with error if it blocks for greater than a specified threshold of time say 500miliSec?

I am not willing to use Non-Blocking I/O calls as I am recording I/O failure count and terminating the process if the count exceeds a limit, and Non-Blocking I/O rapidly takes the I/O error count beyond threshold.

Community
  • 1
  • 1
Anirban
  • 550
  • 3
  • 19
  • 4
    Reading from disk blocks for seconds? How comes? Is this due to amount of data or due to competing accesses? Elaboration of this may help to solve your issue. – Scheff's Cat Apr 09 '18 at 05:36
  • 3
    Am I reading this right, that you have some read/write calls to the disk and they take over 500ms? Because that sounds like a different kind of issue than setting timeouts... – viraptor Apr 09 '18 at 05:36
  • 3
    You could also still use non-blocking I/O. The return value should be something like `EAGAIN`, so you can make sure to check that and not increment your error counter in that case. – bnaecker Apr 09 '18 at 05:38
  • Possible duplicate of [C++0x thread interruption](https://stackoverflow.com/questions/2790346/c0x-thread-interruption) – Mikhail Apr 09 '18 at 05:39
  • 2
    @Mikhail this question has nothing yo do with threads. – hyde Apr 09 '18 at 06:05
  • 2
    Something funny going on. We need an MCVE. – hyde Apr 09 '18 at 06:06
  • 1
    You could set a signal with `alarm` to interrupt your calls and check for `EINTR` when calling `read` and `write`, but if `select` isn't working something fishy is going on here... – Matteo Italia Apr 09 '18 at 06:11
  • 4
    Writing to the file system/disk on most Unixy systems to most filesystems is non-interruptible. There is no way to get timeouts. Signals won't interrupt the `read`/`write`, non-blocking I/O won't do anything (things will still block if you manage to set non-blocking on the fd at all). You're out of luck. Either repair your system (because waiting for disk I/O for several seconds sounds very broken) or move writing to the disk to some separate thread/process and manage timeouts that way. (I'm using "most" above because there are exceptions, like soft NFS mounts or userland filesystems or such). – Art Apr 09 '18 at 07:01
  • Maybe you can try [aio](http://man7.org/linux/man-pages/man7/aio.7.html) for non-blocking disk io. Start a read/write and then use aio_suspend() to wait for its completion with a timeout. – 王致远 Apr 09 '18 at 07:08
  • @hyde 'nothing yo do with threads' and the disk calls are being made by..... what? – Martin James Apr 09 '18 at 07:10
  • Please clarify: '500mSec?' do ylou mean half a second, or half a millisecond? 500ms is easy, 500ns is awkward. – Martin James Apr 09 '18 at 07:14
  • Disk calls getting blocked for an extended period can happen. SSD's sometimes go deaf and dumb while doing internal bookeeping, networked disk calls can occasionally take ages. – Martin James Apr 09 '18 at 07:52
  • Well, like @Art says, if you are willing to thread off the calls by substituting'timeoutRead()', 'timeoutWrite()' calls instead, you can surely build a mulithtreaded subsystem that will make the blocking calls for you and return an error if the interval is exceeded without those calls returning. You need a bit of care, (if the timeout happens, you have to ensure that the thread that made the blocking call can no longer return any result if the blocked call returns 'immediately after' or later), but it's certainly doable. – Martin James Apr 09 '18 at 11:30
  • Without a very careful design a thread doing the I/O it might make things worse. One of the points of timeouts is that they cancel an operation. A normal timeout means "don't waste more time trying to finish this", a side effect of it is that we lower the pressure on the bottleneck. That is not really achievable on normal file operations. So if the thread does the same amount of reads and writes, the same amount of reads and writes will hit the disk and the disk will keep being under the same load and the only result is that otherwise successful I/O is wasted. – Art Apr 09 '18 at 12:55

2 Answers2

2

There is no general way to set a timeout on blocking file descriptors but if you are dealing with sockets, there are socket options that you can use.

An alternative can be to set an alarm. This will raise a signal when it expires that you can use to close the socket an generate the necessary errors.

doron
  • 27,972
  • 12
  • 65
  • 103
  • While a read() blocked on Disk I/O, will a signal abort the call and return with `errno` set to some value? Or do I need to do something in the signal handler to mimic the above? As, I was thinking to set an `ualarm()` for 500mSec and go for the `read()` call and have a empty handler declared for `SIGALRM`. – Anirban Apr 11 '18 at 03:54
  • You need to close the fd to unblock it then return. – doron Apr 11 '18 at 05:41
0

The answer is specific to the OS because C++ doesn't abstract thread termination.

Note that functions can only be killed mid-flight by an interrupt mechanism. After the thread is marked for deletion by TerminateThread, after some number of microseconds (controlled by NT interrupt latency commands) the OS will acquire control of the CPU executing the thread. It may actually be difficult to get this down to 500 microseconds, furthermore, the latency will vary system to system. See DPC latency and tools like DPC Latency Checker.

Also I suspect something terrible will happen to the IO handles, meaning subsequent commands to them might result in a fault.

#include <thread>
#include <iostream>
#include <windows.h>
int main(int argc, char *argv[])
{
    auto thread_to_run_the_command_on = std::thread( [] {
        std::cout << "Started" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));// Your function goes here
        std::cout << "Never Called" << std::endl;
        // thread floats away into the aether, also RAII is violated for objects in this scope
        });
    //give it some time to start
    std::this_thread::sleep_for(std::chrono::seconds(3));
    const auto windows_native_thread_handle = thread_to_run_the_command_on.native_handle();
    TerminateThread(windows_native_thread_handle, 0);
    thread_to_run_the_command_on.detach();
}
Mikhail
  • 7,749
  • 11
  • 62
  • 136
  • 1
    As far as I can see, this question has nothing to do with threads. – hyde Apr 09 '18 at 06:07
  • @hyde To summarize the guy has a function that calls some library and needs the function to terminate if its taking too long. There is no way to kill a function mid flight without killing the thread its on. – Mikhail Apr 09 '18 at 06:10
  • 1
    A C++ answer for Windows isn't probably the best answer to a C question tagged Unix. – alk Apr 09 '18 at 06:13
  • 1
    @alk originally it was tagged as C++ (that's how I found it). But, yes guessing what this guy wants is pretty hard, and I think we should close it. – Mikhail Apr 09 '18 at 06:15
  • 1
    '500 microseconds' OP posted '500mSec'. This is ambiguous, and needs clearing up. 500us is a very short interval to time out with kernel signaling, and very long for polling loops. 500ms, however, is easy for a kernel-based timeout. – Martin James Apr 09 '18 at 07:13
  • 500mSec is 500 mili-Seconds (updated the question), 500 uSec is ofcourse too small for an I/O operation. My code can tolerate a delay of around 1 sec, but to be safe I would like to set the threshold based on statistics which tells me almost all `read()s` on my system are completing in 100 miliSec and to be more liberal I am setting it to 500. – Anirban Apr 13 '18 at 06:25