Skip to content

IOSink.flush() - Bad state: StreamSink is bound to a stream #25277

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
adamlofts opened this issue Dec 16, 2015 · 3 comments
Closed

IOSink.flush() - Bad state: StreamSink is bound to a stream #25277

adamlofts opened this issue Dec 16, 2015 · 3 comments
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io

Comments

@adamlofts
Copy link

This code writes to a file and calls flush() a lot. It does not wait for the flush future to complete. The intent is to signal the os to write out the data so it becomes visible to other processes which have the file open.

import 'dart:async';
import 'dart:io';
import 'dart:convert';

void main() {

    File file = new File("test.txt");
    IOSink io = file.openWrite(mode: FileMode.APPEND);
    for (int i = 0; i < 1000; i += 1) {
        io.write("HOLA");
        io.flush();
    }
}

Running this in dart produces the crash:

Unhandled exception:
Bad state: StreamSink is bound to a stream
#0      _StreamSinkImpl._controller (dart:io/io_sink.dart:223)
#1      _StreamSinkImpl.add (dart:io/io_sink.dart:146)
#2      _IOSinkImpl.write (dart:io/io_sink.dart:281)
#3      main (file:///home/adam/dev/bet/test.dart:12:12)
#4      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:263)
#5      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:150)

Am I using the wrong API for this or is it a bug?

@floitschG floitschG added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io labels Dec 16, 2015
@whesse
Copy link
Contributor

whesse commented Dec 16, 2015

The flush method does not sent any signals to the io system, or push data through any faster. It just returns a future that completes when there is no data buffered in the IOSink object. It essentially just adds an empty stream to the IOSink, and signals when this empty stream is completed.

Flush does not push data any faster, it just reports when there is no data buffered in this object.
And you must wait on the future returned by Flush to get that information.

So, just as only one stream can be added to an IOSink (or any StreamConsumer) at a time, only one flush operation can be outstanding at a time.

@whesse
Copy link
Contributor

whesse commented Dec 16, 2015

This message extends the earlier comment, with more details about how flush() could, in some cases, signal the IOSink to move data.

IOSink is built upon the simpler class StreamConsumer, which can only take streams of data, one at a time, and consume them. At the end of each stream, it gets a done event from that stream, which it COULD use as a signal to flush stuff more aggressively into or through whatever its target is. Then, it completes the future it returned when the stream was added, to signal it is done and ready to take another stream. So this future completing COULD indicate that the data has been fully processed downstream, in some sense.

The synchronous add() method is added to this object that only supports adding a stream, by adding a hidden internal streamController, and adding its stream to the StreamConsumer. Sequential add() calls just have their data all fed into this streamController, and this hidden internal stream is closed only when the whole IOSink is closed, or when addStream() or flush() is called on it. The added stream is stored, the hidden internal stream is closed, and when the StreamConsumer is ready to take a new stream, the added stream is added. So it is possible that calling flush(), by closing the internal stream, may signal to the downstream StreamConsumer that it should "flush" in some way.

But just as only one added stream can be active, only one flush can be active, since it acts as an added stream.

@whesse whesse closed this as completed Dec 16, 2015
@adamlofts
Copy link
Author

So, just as only one stream can be added to an IOSink (or any StreamConsumer) at a time, only one flush operation can be outstanding at a time.

The exception thrown above is from a call to IOSink.add so it seems like you can not call add whilst a flush operation is outstanding. Suggest documenting this and improving the exception message if possible.

For my own application I now see that I don't need to call the flush method at all. I was used to having to call flush() in python because python does some internal buffering before calling the underlying os write().

If you were implementing a db then you would want to be able to call fsync on a stream which is not possible. There is a closed bug about it which seems to also be confused about what the flush method is doing: #8794

andrewkolos added a commit to andrewkolos/flutter that referenced this issue Jan 30, 2024
AR-CADE added a commit to AR-CADE/dart_swaymsg that referenced this issue May 13, 2024
…e since it will not serve to optimize the buffer. It is also called inside a stream subscription that is it surely not good and moreover may cause a `Bad state: StreamSink is bound to a stream` in this context
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io
Projects
None yet
Development

No branches or pull requests

3 participants