in reply to Re^3: Win32::MMF + threads misbehavior
in thread Win32::MMF + threads misbehavior

I guess we'll have to agree to disagree, as my experience of sockets (or even pipes) vs. shared memory (including of the mmap'ed kind) is very much different than yours (ie, the latter is *very* much simpler and faster, once its setup).

As to locking, as I mentioned earlier, I do as little of it as possible: each thread gets its own region for a ring buffer, so the only synchronization required is at thread create time in order to allocate a region from the region map created by the root process/thread the first time it gets into DB::DB. Once thats done, no thread (or process) ever gets in the way of another. A simple thread lock (plus a file lock on *nix systems) covers the allocation step (tho admittedly not well on Win32, but fork() on Win32 is an odd duck anyway which I'm happy to ignore for the present).

The plstrace app doesn't do any locking - it just reads as it needs, checks that the data looks reasonable, and prints it. This isn't a transaction mgr, just a tool to peek at whats going on inside a running app; a little garbage in the stew won't hurt anything.

The most troubling issue is that this is behaving oddly on Win32, an OS that primarily relies on threads (rather than processes) for concurrency, and on memory mapped files for shared memory. So I'd expect things at the system call level to behave a bit more sensibly. Which leads me to believe that the cloning of the mmap'ed tie() may well be doing the damage.

Replies are listed 'Best First'.
Re^5: Win32::MMF + threads misbehavior
by BrowserUk (Patriarch) on Apr 06, 2006 at 04:12 UTC
    The most troubling issue is that this is behaving oddly on Win32, an OS that primarily relies on threads (rather than processes) for concurrency, and on memory mapped files for shared memory.

    I may be misinterpreting you here, so I'll apologise is advance in case I am, but...memory mapped files are not used to share memory between threads. All memory, including MMFiles, is automatically(*) accessible to every thread in the process. The fact that a piece of memory may be backed (or not) by a file is completely transparent to all threads within the process. If you knew that, then no harm done :) If you didn't, it might clear up a misunderstanding.

    (*) It's theoretically possible to allocate memory in one thread with a different set of security attributes to another thread in that same process and create the situation where that memory would be inaccessible to the other thread...but you'd have to work at it to create the situation.

    Which leads me to believe that the cloning of the mmap'ed tie() may well be doing the damage.

    That's part of what I've been trying to get at--but only a part.

    Sharing tied vars through the cloning process suffers the same problems as sharing objects that way. After all they are basically the same thing with a predefined and restricted set of methods. Whilst you can get away with sharing cloned objects between threads (by reblessing), because the cloned object handles act as proxies for the real data, in the case of Win32::MMF objects (and by implication Win32::MMF::Sharable ties), it gets awfully fuzzy because you will effectively be mapping the file into the process twice (or more times). As I said earlier, I'm not exactly sure, and I can't find anything online that discusses it, what will happen at the OS level in that case. It might work fine, and give you the same handle back. It might give you a second handle to the same piece of memory and still work fine. Or it might get awfully confused, I know I am :)

    I simply do not know, but combining that uncertainty, with the vagaries of sharing a tie between threads leaves me in a place where I couldn't even hazard a guess as to what would be the outcome. I can't even get a handle on how to go about testing it? Given that your intention is to use this to try and debug other threads/processes/shared memory, I don't see how you would ever be able to arrive at a confidence level that would allow you to decide that your debug module wasn't exacerbating whatever problems already exist in the programs you are trying to debug.

    You say that plstrace program doesn't use any locking, you mention a ring buffer, and say that a little garbage in the stew won't hurt anything", but I don't see how you can draw any conclusions from what you see in snapshots taken of several asynchronous processes/threads, without knowing the order in which the memory you are looking at came to arrive at the values you will see. Actually, I'm not even sure how you would decide what was garbage and what was stew?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      1. AFAIK, "cannot tie() threads::shared variables" ne "cannot clone tie()'d variables"

      I'm not using threads::shared anything. I create a mmf-tied variable in the root thread/process. When a new thread is spawned, it should clone everything, including the tied variables. If this assumption is false, then all of ithreads - including the Win32 fork() emulation - pretty much drops dead wrt tie(). (I can't seem to find the reference for the "can't tie threads::shared variables", but I know it exists; I'm not aware of a similar restriction wrt non-shared tie()'d variables..if it exists please advise of the docs which say so. I do realize that a tie may require a CLONE method, and perhaps thats whats lacking in Win32::MMF::Shareable)

      2. I don't need to share the memory between the threads, other than to manage the region allocation map. But I do need to share the memory with an external process, namely, plstrace, that inspects the mmf to dump it for inspection.

      3. I'm re-mapping because prior problems led me to Re: Perl forking and shared memory strangeness under Windows (you may recognize the author ;^). With the remapping, I'm able to get a bit further than before. (Interestingly, I had to do the same thing for Sys::Mmap in Linux, tho wo/ re-require'ing, nor re-opening the file, just re-mmap()ing).

      4. As to plstrace's "safety", suffice to say I'm not concerned. The data being written by DB::DB is a reasonably fixed format, every thread of every process only accesses its own segment of the mmf, so there's no overwrites. Its no different than 2 processes writing to the same file, but the first only ever uses the first 10K, and the 2nd only ever uses the 2nd 10K. As to the "garbage", plstrace won't have much difficulty sorting it out, and deciding whether to ignore a timestamp or a string-length indicator because its not valid. (FWIW, I've done this sort of thing many times before in C)

      So it boils down to:

      a) does ithreads cloning properly handle tie()'d variables

      b) does Win32 handle multiple mmf's to the same file (why it should matter seems odd to me, but I suppose its possible the page table mapping onto the filesystem buffers might preclude duplicate page table entries mapped to the same physical address)