What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Short answer: fcntl and msvcrt are not interchangeable Python APIs. fcntl is available on Unix-like systems and exposes whole-file flock operations plus byte-range lockf operations. msvcrt is Windows-only and locks a specified byte range. Code that must run on both platforms must choose an implementation at runtime or use an abstraction such as Portalocker. Whichever API you use, every cooperating process must open the same lock target, follow the same protocol, and hold the lock for the complete critical section.
What file locking is supposed to protect
A file lock coordinates processes; it does not automatically make a file transactional, durable, or safe across every filesystem. The usual goal is to protect a critical section such as reading a counter, changing it, and writing it back. Without coordination, two processes can read the same old value and overwrite one another’s updates.
Before choosing an API, decide which of these problems you actually have:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match- Single-owner access: only one process may enter a section at a time. This normally calls for an exclusive lock.
- Concurrent readers: several readers may proceed, but a writer must wait. This requires a shared/exclusive protocol and a primitive that supports the required lock modes.
- Byte-range coordination: independent records occupy different regions of one file. A range lock can protect only the bytes belonging to a record.
- Presence or leader signalling: a process needs to advertise ownership or liveness. A lock file can be part of that design, but the lock itself is still an operating-system coordination mechanism.
The standard library does not offer one identical file-locking API on every operating system. Importing fcntl unconditionally is therefore not a Windows solution, and importing msvcrt unconditionally fails on Unix-like systems.
#1 Best Overall
fcntl on Unix-like systems
Python’s fcntl module is an interface to Unix fcntl() and ioctl() facilities. It is available on Unix-like platforms, not on Windows. Two interfaces are especially relevant to file locking.
fcntl.flock: whole-file operations
fcntl.flock(fd, operation) requests an advisory lock operation on an open file descriptor. Typical flags are LOCK_SH for shared access, LOCK_EX for exclusive access, and LOCK_UN to unlock. Add LOCK_NB when the caller should fail rather than wait.
“Whole-file” describes the scope of the flock request: the lock applies to the file represented by the descriptor rather than to a caller-selected offset and length. The lock is still a protocol. On POSIX systems, the usual flock contract is advisory, meaning a process that never attempts to acquire the lock can continue to read or write. Every participant that must coordinate has to honor the convention.
fcntl.lockf: record-lock wrapper
fcntl.lockf(fd, cmd, len=0, start=0, whence=0) wraps record-lock operations. The len, start, and whence arguments let you describe a byte range. A length of zero conventionally means from the starting position to the end of the file. Commands include shared, exclusive, unlock, and non-blocking variants, subject to the platform’s implementation.
lockf and flock are different locking families. Their ownership, inheritance, and close behavior are not interchangeable on every Unix system. Do not have one process use flock while another assumes that a lockf request protects the same critical section. Pick one primitive and use it consistently for all participants.
Blocking and non-blocking behavior
A blocking exclusive request waits until an incompatible lock is released. A non-blocking request returns immediately with an OSError when the lock cannot be obtained. If your application treats contention as an expected condition, handle that condition separately from permission failures, a missing path, a bad descriptor, or a full disk. For portable error handling, inspect the documented errno values for the target platform rather than assuming one exception class or numeric value everywhere.
Rank #2
msvcrt.locking on Windows
Python’s msvcrt module is available on Windows. Its locking(fd, mode, nbytes) function locks a portion of a file identified by a C runtime file descriptor, the current file position, a locking mode, and a byte count.
Recommended Free Tools
The range is central to the API. Position the descriptor at the first byte to protect, then pass the number of bytes. Modes include blocking and non-blocking forms such as LK_LOCK and LK_NBLCK, with LK_UNLCK used to release the range. The exact mode and retry behavior are Windows-specific; read the Python version’s msvcrt documentation when you need a particular timeout or failure guarantee.
Because the API locks a selected range, two callers can accidentally protect different bytes and proceed concurrently. A common lock-file convention is to ensure a one-byte file exists, seek to offset zero, and have every participant lock that same byte. If you instead lock data records, document the offsets and lengths so that every reader and writer calculates them identically.
Windows locking behavior should not be described as a drop-in equivalent to POSIX advisory flock. The standard-library documentation specifies the byte-range interface; enforcement and sharing details belong to the Windows file-opening and filesystem model. Test the exact combination of access flags, sharing modes, and filesystem used by your application.
Decision table: fcntl versus msvcrt
| Decision axis | fcntl.flock / fcntl.lockf |
msvcrt.locking |
|---|---|---|
| Availability | Unix-like Python builds; the module is not provided on Windows. | Windows Python builds; the module is not a Unix replacement. |
| Scope | flock is whole-file; lockf supports a selected range through length, start, and origin arguments. |
A selected range beginning at the descriptor’s current position and extending for the requested byte count. |
| Shared locks | flock and lockf expose shared-lock flags, subject to platform behavior. |
The basic API is range-oriented; a true shared-lock design may require a Windows-specific mechanism or a library abstraction. |
| Non-blocking mode | Add LOCK_NB; an unavailable lock is reported as an OSError condition. |
Use a non-blocking mode such as LK_NBLCK; handle the documented Windows error condition. |
| Portability | Requires Unix-specific code or a wrapper. | Requires Windows-specific code or a wrapper. |
A minimal cross-platform lock with the standard library
The following example uses a persistent lock file and protects one byte. It chooses the implementation only after checking the operating system, keeps the lock across the entire critical section, and releases it in a context manager. The one-byte convention is deliberate: all cooperating processes must use the same path and the same byte.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →from contextlib import contextmanager
import os
import sys
@contextmanager
def exclusive_lock(path, blocking=True):
# a+b permits reading and writing and does not truncate an existing file
with open(path, 'a+b') as lock_file:
# msvcrt.locking needs a byte to exist in the file
if os.path.getsize(path) == 0:
lock_file.write(b'0')
lock_file.flush()
lock_file.seek(0)
if os.name == 'nt':
import msvcrt
mode = msvcrt.LK_LOCK if blocking else msvcrt.LK_NBLCK
try:
msvcrt.locking(lock_file.fileno(), mode, 1)
except Exception:
# The file object still closes below; do not hide the
# original locking or I/O error from the caller.
raise
try:
yield lock_file
finally:
lock_file.seek(0)
msvcrt.locking(lock_file.fileno(), msvcrt.LK_UNLCK, 1)
else:
import fcntl
operation = fcntl.LOCK_EX
if not blocking:
operation |= fcntl.LOCK_NB
fcntl.flock(lock_file.fileno(), operation)
try:
yield lock_file
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
# Keep every read/modify/write step inside this block.
with exclusive_lock('/var/tmp/my-app.lock'):
update_shared_state()
This is a teaching pattern, not a universal production recipe. The path must be writable on both platforms; /var/tmp is only an example and is not a portable location. Choose an application-specific directory with known permissions, and make sure every process resolves the same absolute path. Relative paths can identify different files when processes start in different working directories.
Why the lock surrounds the whole operation
Opening the file, acquiring a lock, reading state, computing a new value, writing, flushing, and releasing are one logical transaction in this example. Moving the unlock before the write recreates the race the lock was intended to prevent. If the operation raises, the finally block still attempts to release the lock and the outer with statement closes the descriptor.
Making contention a normal branch
For a try-once operation, call exclusive_lock(path, blocking=False) and catch the platform’s documented “would block” error. Report “another process is using the resource” separately from a permission error or a corrupt path. If you need a timeout, implement a bounded retry loop with a monotonic deadline and a short sleep; do not busy-spin. A retry loop should still distinguish a lock that remains busy from an I/O failure that will not recover by waiting.
Using a shared abstraction instead of branching
Portalocker presents a common Python-facing interface and selects platform mechanisms underneath. Its documentation describes POSIX use of fcntl.flock (or optional lockf) and Windows use of msvcrt by default. This removes import branching from application code, but it does not remove the need to understand lock scope, advisory behavior, paths, or failure handling.
For a basic context-managed lock, the shape is:
import portalocker
with portalocker.Lock('my-app.lock', timeout=10):
update_shared_state()
Install the package according to the version you have selected. Portalocker documents the optional Windows extra for shared-lock support with the command python -m pip install 'portalocker[win32]'. Its documentation states that ordinary Windows exclusive locks work without that extra, while shared Windows locks use optional Win32 support. Confirm the installed release and supported Python range before copying a command into deployment documentation because package behavior and requirements can change.
The library also documents several distinct lock classes, including an ordinary file lock, a reentrant lock, a temporary-file mutex, a PID-file lock, and a bounded semaphore. They solve different coordination problems. A PID-file lock, for example, carries process-identity concerns that are not present in a simple critical-section lock; a semaphore limits a count of simultaneous users rather than enforcing one owner. Choose the class that matches the resource, rather than treating every class as a spelling variation of the same lock.
Lock-file design details that prevent subtle races
Keep the lock path stable
Every participant must use the same file identity. Locking data.json.lock in one process and ./data.json.lock from a different directory may select different files. Symlinks, case differences on case-insensitive filesystems, container mount paths, and per-user temporary directories can also defeat an otherwise correct protocol. Resolve and configure one canonical location where practical.
Do not delete a lock file as a routine cleanup step
The operating-system lock is released when the descriptor is explicitly unlocked and normally when the process closes it. The directory entry can remain. Keeping a zero- or one-byte lock file avoids a race in which one process unlinks the path while another still holds an open descriptor, after which a third process creates a new file and locks a different inode. Remove a lock file only with a design that proves no process can still be using it.
Open with compatible access
The descriptor must be opened in a mode that permits the intended operation. A read-only descriptor may be unsuitable for a Windows locking call or for a protocol that writes a marker. Conversely, opening a data file for update can alter its metadata or truncate it if the wrong mode is chosen. A separate lock file is often safer because locking it does not change the contents of the protected data file.
Use one primitive and one range convention
All participants should agree on flock versus lockf, or on the exact Windows range and mode. Mixing primitives is not portable, and a library’s wrapper may make a different range choice than hand-written code. Document the convention beside the code and in the operational runbook.
Advisory locks, enforcement, and bypasses
On Unix-like systems, an advisory lock works only when every relevant process checks it. A process that opens the data file and writes without acquiring the agreed lock can still interfere. This is why the lock protocol belongs in every reader and writer, including maintenance scripts and one-off migration tools.
Do not infer that a successful local test proves distributed mutual exclusion. Network filesystems, SMB shares, NFS mounts, cloud-synchronized folders, and container volumes can have implementation-specific lock behavior, caching, failover, and disconnect semantics. The available documentation for these APIs does not establish a universal guarantee for every such environment. If the data crosses a network boundary, validate the exact operating system, mount options, server implementation, and failure model before making a deployment guarantee.
Testing a locking implementation
A useful test launches two independent processes that use the same lock path and deliberately hold the lock for a visible interval. The second process should block (or receive the documented non-blocking failure) until the first releases it. Add tests for:
Best Value
- normal release after a successful critical section;
- release when the protected function raises an exception;
- two processes started from different working directories;
- blocking and non-blocking contention;
- an absent lock file and a pre-existing lock file;
- permission errors and a read-only directory;
- the same byte-range convention on Windows;
- the exact filesystem used in production, if it is not local.
Run the suite on both a Unix-like host and Windows. A test that merely imports both modules on one platform misses the most common portability failure: the unavailable module is imported before the platform branch runs.
Common mistakes and their fixes
Importing both modules at the top level
Symptom: a Windows process fails with an import error for fcntl, or a Unix process fails when code expects msvcrt. Fix: import the platform module inside a branch, or use a maintained abstraction.
Locking different files
Symptom: both processes report success even though each appears to acquire a lock. Fix: log the canonical lock path and verify that all processes use the same file identity and permissions.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteProtecting only part of a transaction
Symptom: occasional lost updates remain after adding a lock. Fix: move the lock acquisition before the first read and release it only after the final write and required flush.
Assuming a lock makes uncooperative code safe
Symptom: a script that bypasses the protocol still corrupts data. Fix: make every writer and reader follow the protocol, or use an enforcement mechanism appropriate to the operating system and threat model.
Using a stale-lock timeout as proof of process death
Symptom: a timeout-based cleanup removes a file while a legitimate process still owns the lock. Fix: distinguish the persistent pathname from the live OS lock, and use a documented PID-file or lease design only when you can validate process identity and failure cases.
Quick Recap
How to choose an approach
| Situation | Recommended shape | Reason |
|---|---|---|
| Unix-only program protecting one shared file | fcntl.flock with a separate lock file |
Simple whole-file coordination with explicit shared or exclusive modes. |
| Unix program updating independent records | fcntl.lockf with documented ranges |
Record-lock arguments express byte offsets and lengths. |
| Windows-only program with a known byte range | msvcrt.locking |
The API directly specifies the starting position and byte count. |
| One codebase for Unix and Windows | A small platform branch or Portalocker | Keeps OS-specific imports and semantics in one place. |
| Shared locks on Windows | An abstraction with the documented Windows support installed | The basic msvcrt interface is not a portable true shared-lock solution. |
Practical checklist
- Identify whether you need exclusive, shared, or byte-range coordination.
- Choose Unix-specific code, Windows-specific code, or an abstraction deliberately.
- Use one canonical lock path and document it.
- Ensure the file is opened with access compatible with the operation.
- Acquire the lock before the first read in the critical section.
- Keep it until the final write and flush are complete.
- Release it in
finallyor a context manager. - Handle expected contention separately from permission and I/O failures.
- Do not mix
flock,lockf, and Windows range conventions casually. - Test normal, exceptional, non-blocking, and cross-process paths on every supported platform.
- Qualify any network-filesystem guarantee with the exact filesystem and mount configuration.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Free tools Windows power users keep installed
One-click scans. No signup required.

