It was six weeks from when I teased an Okio code sample, to releasing the project publicly. In that time I went back and forth over and over on how to do timeouts.
The lightbulb came when I discovered that we were addressing two different problems:
- Detecting network partitions. That way we can recover from common I/O problems like bad WiFi connections, crashed modems, and unresponsive servers.
- Limit how much time is spent on a task. We might want to preload images without wasting too much battery.
In the first scenario, any connectivity is good connectivity. When we receive just a single byte we know that the remote peer is alive and reachable. If we don't see any activity on the other end for a long time, we can assume the worst and terminate the connection.
The second scenario applies even if the network is operating perfectly. If we exceed the time budgeted for the task we cancel the task.
Okio implements both of these strategies, referred to in the API as timeouts and deadlines respectively. You can even use Okio to set up write timeouts for blocking sockets:
Socket socket = new Socket("square.com", 90);
BufferedSink sink = Okio.buffer(Okio.sink(socket));
sink.timeout().timeout(5, TimeUnit.SECONDS);
OutputStream out = sink.outputStream();
BufferedSource source = Okio.buffer(Okio.source(socket));
source.timeout().timeout(5, TimeUnit.SECONDS);
InputStream in = source.inputStream();
Internally timeouts use a helper watchdog thread to snipe sockets that take longer than they're permitted. The implementation was fun to write because it coordinates threads without making work for the garbage collector.