On Tue, 14 Feb 2012 23:10:11 -0500
Mike Meyer<[email protected]>  wrote:

I'd prefer to provide shm_open on Windows if at all possible. The
"sorta-kinda" bothers me. That would also allow for an application to
exit and then resume work stored in a mapped segment (something I've
done before). However, setting this up on Windows isn't something I
can do.

Here is a proof-of-concept for shm_open/shm_unlink on Windows.

Note that shm_unlink opens the file using FILE_FLAG_DELETE_ON_CLOSE which ensures that subsequent attempts to open the file will not succeed. However, the directory entry will not disappear till all handles have been closed.

FILE_ATTRIBUTE_TEMPORARY tells the system we want to try to cache the file without worrying about flushing.

sbt


import os
import msvcrt
import tempfile
from _multiprocessing import win32

DEV_SHM = tempfile.gettempdir()

GENERIC_READ = win32.GENERIC_READ
GENERIC_WRITE = win32.GENERIC_WRITE

CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5

FILE_SHARE_READ = 1
FILE_SHARE_WRITE = 2
FILE_SHARE_DELETE = 4

FILE_ATTRIBUTE_TEMPORARY = 256
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000

NULL = 0

_CreationDisposition = {
    os.O_CREAT | os.O_EXCL:     CREATE_NEW,
    os.O_CREAT | os.O_TRUNC:    CREATE_ALWAYS,
    0:                          OPEN_EXISTING,
    os.O_CREAT:                 OPEN_ALWAYS,
    os.O_TRUNC:                 TRUNCATE_EXISTING
}

_DesiredAccess = {
    os.O_RDONLY:                GENERIC_READ,
    os.O_RDWR:                  GENERIC_READ | GENERIC_WRITE
}

_OsfDesiredAccess = {
    os.O_RDONLY:                os.O_RDONLY,
    os.O_RDWR:                  0
}

def _get_path(name, dev_shm):
    name = name.lstrip('/')
    if '/' in name or '\\' in name:
        raise ValueError('invalid name')
    return os.path.join(dev_shm, name)

def shm_open(name, flags, mode, dev_shm=DEV_SHM):
    # Opening with FILE_SHARE_READ | FILE_SHARE_WRITE |
    # FILE_SHARE_DELETE ensures that other processes can open the file
    # and pseudo-unlink it while it is still in use.
    path = _get_path(name, dev_shm)
    da_flags = flags & (os.O_RDONLY | os.O_RDWR)
    cd_flags = flags & ~(os.O_RDONLY | os.O_RDWR)
    h = win32.CreateFile(
        path, _DesiredAccess[da_flags],
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
        _CreationDisposition[cd_flags], FILE_ATTRIBUTE_TEMPORARY, NULL)
    try:
        os.chmod(path, mode)
        return msvcrt.open_osfhandle(h, _OsfDesiredAccess[da_flags])
    except:
        win32.CloseHandle(h)
        raise

def shm_unlink(name, dev_shm=DEV_SHM):
    # Opening with FILE_FLAG_DELETE_ON_CLOSE will make subsequent
    # attempts to open the file fail.  However, the name is not
    # removed from the file system until all handles have been removed.
    path = _get_path(name, dev_shm)
    h = win32.CreateFile(
        path, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL)
    win32.CloseHandle(h)

##

if __name__ == '__main__':
    import mmap

    MYMAP = "mymmap"

    fd = shm_open(MYMAP, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o600)
    m = mmap.mmap(fd, 10)
    os.close(fd)

    m[:5] = "hello"

    fd = shm_open(MYMAP, os.O_RDWR, 0o600)
    n = mmap.mmap(fd, 10)
    os.close(fd)

    n[:] = n[:].upper()
    n.close()

    assert os.path.exists(_get_path(MYMAP, DEV_SHM))
    shm_unlink(MYMAP)

    try:
        fd = shm_open(MYMAP, os.O_RDWR, 0o600)
    except OSError:
        pass
    else:
        raise AssertionError("expected access denied")

    print repr(m[:])
    m.close()
    assert not os.path.exists(_get_path(MYMAP, DEV_SHM))

_______________________________________________
concurrency-sig mailing list
[email protected]
http://mail.python.org/mailman/listinfo/concurrency-sig

Reply via email to