-
Notifications
You must be signed in to change notification settings - Fork 7
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
Cannot read memory asynchronously, between processes #3
Comments
Hmm, yeah, there are two cases here: a) the change flag is changed by two writer processes. Because this acts as a toggle, it can create a situation where reader processes don't recognize changes. The data might change but the code doesn't "identify" the change. This could be solved by replacing the bit with a CRC checksum and checking the checksum for change. b) two writer processes are writing and overwriting themselves. If the data is "the same", and readers "lack behind" in timing, they wouldn't see the change that happened "in between". Adding a revision number (a number that starts at 0 and increments with every change) would help here to identify "missed changes" on the reader side. If the revision counter increased > +1 for a reader (between the last read and the current read), it's clear that changes are missing. The complicated part here is that also he writer process needs to read the revision number before incrementing. There could still be a RACE condition where two writer processes read the revision number at the same time and increment by +1, overwriting their data. That's why this impl. would also need a lock. To come up with a solid implementation, every writer needs to randomly decide on a process id that with a very high chance is not taken by another writer process. The intuitive choice would be MD5'ing a UUIDv4. The alternative is to synchronize "process IDs" using a temporary file. This would be simpler as process IDs could just be numbers then... The "process id" would be written to the meta data in a lock field. If a writer process reads the revision number and the process id in the lock is not it's own, it needs to wait and re-read it unitil the lock is 0. If the lock is 0, the first writer process that writes it's own process id in the lock field, wins the RACE; reads the then-current revision number, writes its data, increases the revision number, sets the lock to 0 again and so on... to not overflow the revision number, max. 255 revisions would be supported if the revision field has 1 byte length. However even this wouldn't solve the "What's the data that I've missed?" Question. To implement this, the library could allocate I don't have much time these days, but I'd welcome PR's if you like to play with this maybe? |
@funatsufumiya Had a few more thoughts... RACE was still possible with the previous idea; I edited my comment :) |
@kyr0 I was trying to use it like the so-called PUSH/PULL or PUB/SUB, but I was struggling because the data was only loaded correctly the first time. The first thing I thought of was to create a couple of shared memory locations with similar names and swap them with a separate area that points to which one is being used, but I will try the method you adviced me :) |
I also had an idea to create an area that corresponds to a mutex lock, and both the reader and writer use lock there, but it seemed to cause problems such as the locks not being removed permanently, so I would like to try various methods. |
I just came up with the idea for PUSH/PULL style, that the shared memory should have a different (incremental) name each time and the reader should delete the memory once it has been read. That way, the write side would always write to a new area and not have to know the behavior of the read side, which may be the best way for my current needs. (Shared memory that points to the latest address may be necessary.) |
Yep, that would resemble parts of the idea of process_ids basically; you can also simply create a temporary file where the process ids of writers are named in.
Readers need to watch the To identify that, you can simply prefix your actual data with a few bytes (as a prefix) encoding a current timestamp (e.g. ms precision UNIX timestamp) when data is written. The reader then uses this timestamp info to select the most recent one given the current system clock time.
This way you'd basically build a library with a protocol on top of |
@kyr0 Thank you! I'll try them! |
@kyr0 (And also, but much higher level, may you already know) Referenced Issue: |
By the way, if we look at it not as a messaging protocol but in the direction of Ephemeral DB, I think SimDB (and many more) would be the category of high-level implementations. |
I couldn't find this before, but cpp-ipc seems to be an IPC implementation with a simple mechanism like PUBSUB. (The number of receivers seems to be limited to 32.) https://github.com/mutouyun/cpp-ipc (PS: It was not a cross-platform implementation...) |
Looking at |
Oh thank you for all the research you've done. I think we could add those findings to the README.md as well. A section line "Alternatives" could be helpful for others I guess? And yeah, there's a lot of alternatives out there already -- I personally think that simplicity is key for good software quality. The most simple solution that does the job is often the best IMHO.. it allows code to be understood by anyone, adapted, maintained, fixed etc. pp. That's why I sometimes reinvent the wheel on purpose.. just to get to a solution that is waaay simpler than the madness of overcomplexity that we can often find out there :) |
I generally agree, but in this case, many use cases need asynchronous connection from multiple applications, so I wonder that some simple asynchronous support would be good. |
I tested with different processes, but read data won't change. Maybe while other process is writing something. I think some kind of mutex or double buffer is required here.
The text was updated successfully, but these errors were encountered: