fix: native Windows support (msvcrt lock instead of fcntl)
fcntl does not exist on Windows, so memo crashed on import. Guard the import and use msvcrt advisory locking with spin/backoff; open the lock file in append mode so parallel sessions don't break each other's locks. Verified: 8 parallel processes x 200 notes = 1600/1600 records on native Windows (no WSL).
This commit is contained in:
parent
d618a3a626
commit
3411344db6
2 changed files with 55 additions and 3 deletions
22
WINDOWS.md
Normal file
22
WINDOWS.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Windows support
|
||||
|
||||
OptMem now runs on native Windows (no WSL required).
|
||||
|
||||
## What changed
|
||||
- `import fcntl` is guarded — falls back to `None` on platforms without it.
|
||||
- `locked()` uses `msvcrt` advisory locking with spin/backoff when `fcntl`
|
||||
is unavailable, so parallel sessions (the documented multi-process case)
|
||||
queue instead of raising `Resource deadlock avoided`.
|
||||
- The `.lock` file is opened in append mode (`"a"`) rather than `"w"`, which
|
||||
would truncate and break locks held by other processes on Windows.
|
||||
|
||||
## Test (Windows native, no WSL)
|
||||
```bat
|
||||
python memo init
|
||||
set MEMORY_DIR=C:\path\to\mem
|
||||
python memo note "first memory"
|
||||
python memo note "second memory"
|
||||
python memo wake
|
||||
```
|
||||
Concurrency: 8 parallel `memo note` processes writing 1600 memories
|
||||
resulted in 1600/1600 records persisted (lock verified).
|
||||
32
memo
32
memo
|
|
@ -16,7 +16,10 @@ See github.com/VictorTaelin/OptMem.
|
|||
"""
|
||||
|
||||
import datetime
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None # Windows has no fcntl; we fall back to msvcrt below
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
|
@ -297,8 +300,35 @@ def pad(text, rec):
|
|||
|
||||
|
||||
def locked(d):
|
||||
lock = open(os.path.join(d, ".lock"), "w")
|
||||
# Open in append mode ("a"), NOT "w": reopening with "w" truncates the
|
||||
# lock file and breaks advisory locks held by other processes on Windows.
|
||||
lock = open(os.path.join(d, ".lock"), "a")
|
||||
if fcntl is not None:
|
||||
fcntl.flock(lock, fcntl.LOCK_EX)
|
||||
else:
|
||||
# Windows: no fcntl. Use msvcrt advisory lock with spin/backoff so
|
||||
# parallel sessions (the documented multi-process case) queue instead
|
||||
# of raising "Resource deadlock avoided" under contention.
|
||||
import msvcrt as _ms
|
||||
import time as _t
|
||||
waited = 0.0
|
||||
while True:
|
||||
try:
|
||||
_ms.locking(lock.fileno(), _ms.LK_NBLCK, 1)
|
||||
break
|
||||
except OSError:
|
||||
if waited > 30.0:
|
||||
raise
|
||||
_t.sleep(min(0.01 + waited * 0.2, 0.25))
|
||||
waited += 0.01
|
||||
_orig_close = lock.close
|
||||
def _close():
|
||||
try:
|
||||
_ms.locking(lock.fileno(), _ms.LK_UNLCK, 1)
|
||||
except Exception:
|
||||
pass
|
||||
_orig_close()
|
||||
lock.close = _close
|
||||
return lock
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue