labstreaminglayer / pylsl

Python bindings (pylsl) for liblsl
MIT License
136 stars 57 forks source link

Closing Inlet with `inlet.close_stream()` does not de-register as "consumer" #43

Closed jackbrookes closed 3 years ago

jackbrookes commented 3 years ago

Hi,

I am using LSL C# to send data, and LSL Python to receive data. I have set up my C# to display a message when consumers are connected (for peace of mind when data are being collected).

However, when I close the inlet stream (or e.g. quit python) the outlet still returns true as having consumers:

outlet.have_consumers(); // true

Am I closing wrong, or something else I am missing?

dmedine commented 3 years ago

This is actually what LSL is supposed to do. The reason your outlet still supports consumers is due to the LSL fail safe system which exists in case a consumer crashes unexpectedly. As far as I know, there hasn't ever been a use case where stream disconnection needed to be reported back to an outlet. Outlets in LSL are sort of 'passive' in that they just sit there announcing themselves and forwarding data if a stream is connected. If a stream disconnects, it will hold on to the last 6 minutes of data by default. Is there any reason you need to know on the outlet side if a stream is disconnected on the inlet side?

It's been a while since I've looked at this, but I would guess that we would have to build a special inlet disconnect method that not only frees the outlet resources but also tells the outlet it is connected to that it is no longer interested in receiving data.

jackbrookes commented 3 years ago

Thanks for the info. I wanted to be able to see on my main PC that triggers are correctly being received on the other side.

It seems to be less of an issue now that I see LSL can automatically re-connect when outlets close/open. Also, if I recreate the outlet, I can see if the inlet is no longer connected re-running have_consumers().

cboulay commented 3 years ago

@jackbrookes This feature of LSL also means that you have to be careful when developing inlets. It's not OK to start pulling in data sample-by-sample and using the number of samples pulled as some sort of clock, deciding to sleep() after each N samples, because you may have a huge backlog of samples waiting for you!

One thing you can do is always call lsl_inlet_flush() immediately after creating an inlet to clear the queue.

Another thing you can do is get in the habit of: (a) always using pull_chunk (not pull_sample) and be able to handle different length return data, and (b) only calling a very short (1-msec) sleep() when pull_chunk returns nothing. If you use this design then your inlet should always be working with the most recent data, at least after the first couple calls to pull_chunk.

dmedine commented 3 years ago

Hmmm. Makes me think that there could be a 'standard_inlet' or some such object that encapsulates this best-practice usage.

jackbrookes commented 3 years ago

@jackbrookes This feature of LSL also means that you have to be careful when developing inlets. It's not OK to start pulling in data sample-by-sample and using the number of samples pulled as some sort of clock, deciding to sleep() after each N samples, because you may have a huge backlog of samples waiting for you!

One thing you can do is always call lsl_inlet_flush() immediately after creating an inlet to clear the queue.

Another thing you can do is get in the habit of: (a) always using pull_chunk (not pull_sample) and be able to handle different length return data, and (b) only calling a very short (1-msec) sleep() when pull_chunk returns nothing. If you use this design then your inlet should always be working with the most recent data, at least after the first couple calls to pull_chunk.

Thanks, don't think it is an issue in my case - I am checking for a message, then acting on it, then immediately waiting again for the next message. Messages are several seconds apart.