* flock.cc (LOCK_OBJ_NAME_LEN): Change to accommodate extra lf_ver
field. (class lockf_t): Add lf_ver field. (lockf_t::lockf_t): Initialize lf_ver to 0. (class inode_t): Change i_wait to i_cnt. Change comment to explain change in usage. (inode_t:use): Rename from wait. Make private. (inode_t::unuse): Rename from unwait. Make private. (inode_t::inuse): Rename from waiting. Make private. (inode_t::notused): New public method to set use count to 0. (inode_t::unlock_and_remove): New method to unlock node and to delete it if it's unused in current process. (fhandler_base::del_my_locks): Drop global list lock. Drop variable no_locks_left. Simpify unlocking and removing node by just calling unlock_and_remove. (fixup_lockf_after_exec): Call notused method for each node. (inode_t::get): Call use method. Lock node only if outside of list lock. (inode_t::get_all_locks_list): Accommodate additional lf_ver field when creating lockf_t structure from object name. (lockf_t::create_lock_obj_attr): Accommodate additional lf_ver field when creating object name from lockf_t structure. Handle STATUS_OBJECT_NAME_COLLISION gracefully in F_POSIX case as well. Change comment accordingly. Increment lf_ver field rather than high byte of lf_wid field. Simplify comment. (fhandler_disk_file::lock): Always call unlock_and_remove rather than just UNLOCK on node. (lf_setlock): Move ret definition where it's used. Drop unneeded tests for obj being not NULL. Only check for deadlock condition if the lock we're trying to establish is a POSIX lock. Revamp object collecting and wait code to cover all cases. Don't return with EDEADLK if blocking process can't be opened for synchronization in F_POSIX case, rather just wait like in F_FLOCK case. Change system_printf to debug_printf in that case. Only run WaitForMultipleObjects with high priority. Close obj and process handles prior to locking node.
This commit is contained in:
parent
36ccb620ec
commit
2e560a092c
|
@ -1,3 +1,41 @@
|
||||||
|
2011-08-29 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* flock.cc (LOCK_OBJ_NAME_LEN): Change to accommodate extra lf_ver
|
||||||
|
field.
|
||||||
|
(class lockf_t): Add lf_ver field.
|
||||||
|
(lockf_t::lockf_t): Initialize lf_ver to 0.
|
||||||
|
(class inode_t): Change i_wait to i_cnt. Change comment to explain
|
||||||
|
change in usage.
|
||||||
|
(inode_t:use): Rename from wait. Make private.
|
||||||
|
(inode_t::unuse): Rename from unwait. Make private.
|
||||||
|
(inode_t::inuse): Rename from waiting. Make private.
|
||||||
|
(inode_t::notused): New public method to set use count to 0.
|
||||||
|
(inode_t::unlock_and_remove): New method to unlock node and to delete
|
||||||
|
it if it's unused in current process.
|
||||||
|
(fhandler_base::del_my_locks): Drop global list lock. Drop variable
|
||||||
|
no_locks_left. Simpify unlocking and removing node by just calling
|
||||||
|
unlock_and_remove.
|
||||||
|
(fixup_lockf_after_exec): Call notused method for each node.
|
||||||
|
(inode_t::get): Call use method. Lock node only if outside of list
|
||||||
|
lock.
|
||||||
|
(inode_t::get_all_locks_list): Accommodate additional lf_ver field
|
||||||
|
when creating lockf_t structure from object name.
|
||||||
|
(lockf_t::create_lock_obj_attr): Accommodate additional lf_ver field
|
||||||
|
when creating object name from lockf_t structure. Handle
|
||||||
|
STATUS_OBJECT_NAME_COLLISION gracefully in F_POSIX case as well.
|
||||||
|
Change comment accordingly. Increment lf_ver field rather than high
|
||||||
|
byte of lf_wid field. Simplify comment.
|
||||||
|
(fhandler_disk_file::lock): Always call unlock_and_remove rather than
|
||||||
|
just UNLOCK on node.
|
||||||
|
(lf_setlock): Move ret definition where it's used. Drop unneeded
|
||||||
|
tests for obj being not NULL. Only check for deadlock condition if the
|
||||||
|
lock we're trying to establish is a POSIX lock. Revamp object
|
||||||
|
collecting and wait code to cover all cases. Don't return with EDEADLK
|
||||||
|
if blocking process can't be opened for synchronization in F_POSIX case,
|
||||||
|
rather just wait like in F_FLOCK case. Change system_printf to
|
||||||
|
debug_printf in that case. Only run WaitForMultipleObjects with high
|
||||||
|
priority. Close obj and process handles prior to locking node.
|
||||||
|
|
||||||
2011-08-27 Corinna Vinschen <corinna@vinschen.de>
|
2011-08-27 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* fhandler.cc (fhandler_base::open): Fix typo in comment.
|
* fhandler.cc (fhandler_base::open): Fix typo in comment.
|
||||||
|
|
|
@ -124,7 +124,7 @@ static NO_COPY muto lockf_guard;
|
||||||
#define INODE_LIST_LOCK() (lockf_guard.init ("lockf_guard")->acquire ())
|
#define INODE_LIST_LOCK() (lockf_guard.init ("lockf_guard")->acquire ())
|
||||||
#define INODE_LIST_UNLOCK() (lockf_guard.release ())
|
#define INODE_LIST_UNLOCK() (lockf_guard.release ())
|
||||||
|
|
||||||
#define LOCK_OBJ_NAME_LEN 64
|
#define LOCK_OBJ_NAME_LEN 69
|
||||||
|
|
||||||
#define FLOCK_INODE_DIR_ACCESS (DIRECTORY_QUERY \
|
#define FLOCK_INODE_DIR_ACCESS (DIRECTORY_QUERY \
|
||||||
| DIRECTORY_TRAVERSE \
|
| DIRECTORY_TRAVERSE \
|
||||||
|
@ -232,6 +232,10 @@ class lockf_t
|
||||||
long long lf_id; /* Cygwin PID for POSIX locks, a unique id per
|
long long lf_id; /* Cygwin PID for POSIX locks, a unique id per
|
||||||
file table entry for BSD flock locks. */
|
file table entry for BSD flock locks. */
|
||||||
DWORD lf_wid; /* Win PID of the resource holding the lock */
|
DWORD lf_wid; /* Win PID of the resource holding the lock */
|
||||||
|
uint16_t lf_ver; /* Version number of the lock. If a released
|
||||||
|
lock event yet exists because another process
|
||||||
|
is still waiting for it, we use the version
|
||||||
|
field to distinguish old from new locks. */
|
||||||
class lockf_t **lf_head; /* Back pointer to the head of the lockf_t list */
|
class lockf_t **lf_head; /* Back pointer to the head of the lockf_t list */
|
||||||
class inode_t *lf_inode; /* Back pointer to the inode_t */
|
class inode_t *lf_inode; /* Back pointer to the inode_t */
|
||||||
class lockf_t *lf_next; /* Pointer to the next lock on this inode_t */
|
class lockf_t *lf_next; /* Pointer to the next lock on this inode_t */
|
||||||
|
@ -239,13 +243,14 @@ class lockf_t
|
||||||
|
|
||||||
lockf_t ()
|
lockf_t ()
|
||||||
: lf_flags (0), lf_type (0), lf_start (0), lf_end (0), lf_id (0),
|
: lf_flags (0), lf_type (0), lf_start (0), lf_end (0), lf_id (0),
|
||||||
lf_wid (0), lf_head (NULL), lf_inode (NULL),
|
lf_wid (0), lf_ver (0), lf_head (NULL), lf_inode (NULL),
|
||||||
lf_next (NULL), lf_obj (NULL)
|
lf_next (NULL), lf_obj (NULL)
|
||||||
{}
|
{}
|
||||||
lockf_t (class inode_t *node, class lockf_t **head, short flags, short type,
|
lockf_t (class inode_t *node, class lockf_t **head,
|
||||||
_off64_t start, _off64_t end, long long id, DWORD wid)
|
short flags, short type, _off64_t start, _off64_t end,
|
||||||
|
long long id, DWORD wid, uint16_t ver)
|
||||||
: lf_flags (flags), lf_type (type), lf_start (start), lf_end (end),
|
: lf_flags (flags), lf_type (type), lf_start (start), lf_end (end),
|
||||||
lf_id (id), lf_wid (wid), lf_head (head), lf_inode (node),
|
lf_id (id), lf_wid (wid), lf_ver (ver), lf_head (head), lf_inode (node),
|
||||||
lf_next (NULL), lf_obj (NULL)
|
lf_next (NULL), lf_obj (NULL)
|
||||||
{}
|
{}
|
||||||
~lockf_t ();
|
~lockf_t ();
|
||||||
|
@ -285,8 +290,11 @@ class inode_t
|
||||||
private:
|
private:
|
||||||
HANDLE i_dir;
|
HANDLE i_dir;
|
||||||
HANDLE i_mtx;
|
HANDLE i_mtx;
|
||||||
unsigned long i_wait; /* Number of blocked threads waiting for
|
uint32_t i_cnt; /* # of threads referencing this instance. */
|
||||||
a blocking lock. */
|
|
||||||
|
void use () { ++i_cnt; }
|
||||||
|
void unuse () { if (i_cnt > 0) --i_cnt; }
|
||||||
|
bool inuse () { return i_cnt > 0; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inode_t (__dev32_t dev, __ino64_t ino);
|
inode_t (__dev32_t dev, __ino64_t ino);
|
||||||
|
@ -302,9 +310,9 @@ class inode_t
|
||||||
void LOCK () { WaitForSingleObject (i_mtx, INFINITE); }
|
void LOCK () { WaitForSingleObject (i_mtx, INFINITE); }
|
||||||
void UNLOCK () { ReleaseMutex (i_mtx); }
|
void UNLOCK () { ReleaseMutex (i_mtx); }
|
||||||
|
|
||||||
void wait () { ++i_wait; }
|
void notused () { i_cnt = 0; }
|
||||||
void unwait () { if (i_wait > 0) --i_wait; }
|
|
||||||
bool waiting () { return i_wait > 0; }
|
void unlock_and_remove ();
|
||||||
|
|
||||||
lockf_t *get_all_locks_list ();
|
lockf_t *get_all_locks_list ();
|
||||||
|
|
||||||
|
@ -320,6 +328,20 @@ inode_t::~inode_t ()
|
||||||
NtClose (i_dir);
|
NtClose (i_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
inode_t::unlock_and_remove ()
|
||||||
|
{
|
||||||
|
UNLOCK ();
|
||||||
|
INODE_LIST_LOCK ();
|
||||||
|
unuse ();
|
||||||
|
if (i_lockf == NULL && !inuse ())
|
||||||
|
{
|
||||||
|
LIST_REMOVE (this, i_next);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
INODE_LIST_UNLOCK ();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
inode_t::del_my_locks (long long id, HANDLE fhdl)
|
inode_t::del_my_locks (long long id, HANDLE fhdl)
|
||||||
{
|
{
|
||||||
|
@ -364,7 +386,6 @@ inode_t::del_my_locks (long long id, HANDLE fhdl)
|
||||||
void
|
void
|
||||||
fhandler_base::del_my_locks (del_lock_called_from from)
|
fhandler_base::del_my_locks (del_lock_called_from from)
|
||||||
{
|
{
|
||||||
INODE_LIST_LOCK ();
|
|
||||||
inode_t *node = inode_t::get (get_dev (), get_ino (), false);
|
inode_t *node = inode_t::get (get_dev (), get_ino (), false);
|
||||||
if (node)
|
if (node)
|
||||||
{
|
{
|
||||||
|
@ -377,19 +398,10 @@ fhandler_base::del_my_locks (del_lock_called_from from)
|
||||||
entry and there are no threads which require signalling, or we have
|
entry and there are no threads which require signalling, or we have
|
||||||
a parent process still accessing the file object and signalling the
|
a parent process still accessing the file object and signalling the
|
||||||
lock event would be premature. */
|
lock event would be premature. */
|
||||||
bool no_locks_left =
|
|
||||||
node->del_my_locks (from == after_fork ? 0 : get_unique_id (),
|
node->del_my_locks (from == after_fork ? 0 : get_unique_id (),
|
||||||
from == after_exec ? NULL : get_handle ());
|
from == after_exec ? NULL : get_handle ());
|
||||||
if (no_locks_left)
|
node->unlock_and_remove ();
|
||||||
{
|
|
||||||
LIST_REMOVE (node, i_next);
|
|
||||||
node->UNLOCK ();
|
|
||||||
delete node;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
node->UNLOCK ();
|
|
||||||
}
|
|
||||||
INODE_LIST_UNLOCK ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called in an execed child. The exec'ed process must allow SYNCHRONIZE
|
/* Called in an execed child. The exec'ed process must allow SYNCHRONIZE
|
||||||
|
@ -408,6 +420,7 @@ fixup_lockf_after_exec ()
|
||||||
allow_others_to_sync ();
|
allow_others_to_sync ();
|
||||||
LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
|
LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
|
||||||
{
|
{
|
||||||
|
node->notused ();
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
cygheap_fdenum cfd (true);
|
cygheap_fdenum cfd (true);
|
||||||
while (cfd.next () >= 0)
|
while (cfd.next () >= 0)
|
||||||
|
@ -428,6 +441,7 @@ fixup_lockf_after_exec ()
|
||||||
{
|
{
|
||||||
lock->del_lock_obj (NULL);
|
lock->del_lock_obj (NULL);
|
||||||
lock->lf_wid = myself->dwProcessId;
|
lock->lf_wid = myself->dwProcessId;
|
||||||
|
lock->lf_ver = 0;
|
||||||
lock->create_lock_obj ();
|
lock->create_lock_obj ();
|
||||||
}
|
}
|
||||||
node->UNLOCK ();
|
node->UNLOCK ();
|
||||||
|
@ -455,13 +469,15 @@ inode_t::get (__dev32_t dev, __ino64_t ino, bool create_if_missing)
|
||||||
LIST_INSERT_HEAD (&cygheap->inode_list, node, i_next);
|
LIST_INSERT_HEAD (&cygheap->inode_list, node, i_next);
|
||||||
}
|
}
|
||||||
if (node)
|
if (node)
|
||||||
node->LOCK ();
|
node->use ();
|
||||||
INODE_LIST_UNLOCK ();
|
INODE_LIST_UNLOCK ();
|
||||||
|
if (node)
|
||||||
|
node->LOCK ();
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
inode_t::inode_t (__dev32_t dev, __ino64_t ino)
|
inode_t::inode_t (__dev32_t dev, __ino64_t ino)
|
||||||
: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_wait (0L)
|
: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_cnt (0L)
|
||||||
{
|
{
|
||||||
HANDLE parent_dir;
|
HANDLE parent_dir;
|
||||||
WCHAR name[48];
|
WCHAR name[48];
|
||||||
|
@ -516,8 +532,8 @@ inode_t::get_all_locks_list ()
|
||||||
if (f.dbi.ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR))
|
if (f.dbi.ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR))
|
||||||
continue;
|
continue;
|
||||||
wchar_t *wc = f.dbi.ObjectName.Buffer, *endptr;
|
wchar_t *wc = f.dbi.ObjectName.Buffer, *endptr;
|
||||||
/* "%02x-%01x-%016X-%016X-%016X-%08x",
|
/* "%02x-%01x-%016X-%016X-%016X-%08x-%04x",
|
||||||
lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid */
|
lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid, lf_ver */
|
||||||
wc[LOCK_OBJ_NAME_LEN] = L'\0';
|
wc[LOCK_OBJ_NAME_LEN] = L'\0';
|
||||||
short flags = wcstol (wc, &endptr, 16);
|
short flags = wcstol (wc, &endptr, 16);
|
||||||
if ((flags & ~(F_FLOCK | F_POSIX)) != 0
|
if ((flags & ~(F_FLOCK | F_POSIX)) != 0
|
||||||
|
@ -537,6 +553,9 @@ inode_t::get_all_locks_list ()
|
||||||
|| ((flags & F_POSIX) && (id < 1 || id > ULONG_MAX)))
|
|| ((flags & F_POSIX) && (id < 1 || id > ULONG_MAX)))
|
||||||
continue;
|
continue;
|
||||||
DWORD wid = wcstoul (endptr + 1, &endptr, 16);
|
DWORD wid = wcstoul (endptr + 1, &endptr, 16);
|
||||||
|
if (!endptr || *endptr != L'-')
|
||||||
|
continue;
|
||||||
|
uint16_t ver = wcstoul (endptr + 1, &endptr, 16);
|
||||||
if (endptr && *endptr != L'\0')
|
if (endptr && *endptr != L'\0')
|
||||||
continue;
|
continue;
|
||||||
if (lock - i_all_lf >= MAX_LOCKF_CNT)
|
if (lock - i_all_lf >= MAX_LOCKF_CNT)
|
||||||
|
@ -547,7 +566,8 @@ inode_t::get_all_locks_list ()
|
||||||
}
|
}
|
||||||
if (lock > i_all_lf)
|
if (lock > i_all_lf)
|
||||||
lock[-1].lf_next = lock;
|
lock[-1].lf_next = lock;
|
||||||
new (lock++) lockf_t (this, &i_all_lf, flags, type, start, end, id, wid);
|
new (lock++) lockf_t (this, &i_all_lf,
|
||||||
|
flags, type, start, end, id, wid, ver);
|
||||||
}
|
}
|
||||||
/* If no lock has been found, return NULL. */
|
/* If no lock has been found, return NULL. */
|
||||||
if (lock == i_all_lf)
|
if (lock == i_all_lf)
|
||||||
|
@ -560,9 +580,9 @@ inode_t::get_all_locks_list ()
|
||||||
POBJECT_ATTRIBUTES
|
POBJECT_ATTRIBUTES
|
||||||
lockf_t::create_lock_obj_attr (lockfattr_t *attr, ULONG flags)
|
lockf_t::create_lock_obj_attr (lockfattr_t *attr, ULONG flags)
|
||||||
{
|
{
|
||||||
__small_swprintf (attr->name, L"%02x-%01x-%016X-%016X-%016X-%08x",
|
__small_swprintf (attr->name, L"%02x-%01x-%016X-%016X-%016X-%08x-%04x",
|
||||||
lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start, lf_end,
|
lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start, lf_end,
|
||||||
lf_id, lf_wid);
|
lf_id, lf_wid, lf_ver);
|
||||||
RtlInitCountedUnicodeString (&attr->uname, attr->name,
|
RtlInitCountedUnicodeString (&attr->uname, attr->name,
|
||||||
LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
|
LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
|
||||||
InitializeObjectAttributes (&attr->attr, &attr->uname, flags, lf_inode->i_dir,
|
InitializeObjectAttributes (&attr->attr, &attr->uname, flags, lf_inode->i_dir,
|
||||||
|
@ -585,27 +605,22 @@ lockf_t::create_lock_obj ()
|
||||||
NotificationEvent, FALSE);
|
NotificationEvent, FALSE);
|
||||||
if (!NT_SUCCESS (status))
|
if (!NT_SUCCESS (status))
|
||||||
{
|
{
|
||||||
if (status != STATUS_OBJECT_NAME_COLLISION || (lf_flags & F_POSIX))
|
if (status != STATUS_OBJECT_NAME_COLLISION)
|
||||||
api_fatal ("NtCreateEvent(lock): %p", status);
|
api_fatal ("NtCreateEvent(lock): %p", status);
|
||||||
/* If we get a STATUS_OBJECT_NAME_COLLISION in the F_FLOCK case, the
|
/* If we get a STATUS_OBJECT_NAME_COLLISION, the event still exists
|
||||||
event still exists because some other process is waiting for it
|
because some other process is waiting for it in lf_setlock.
|
||||||
in lf_setlock. If so, open the event and check the signal state.
|
If so, check the event's signal state. If we can't open it, it
|
||||||
If we can't open it, it has been closed in the meantime, so just
|
has been closed in the meantime, so just try again. If we can
|
||||||
try again. If we can open it and the object is not signalled,
|
open it and the object is not signalled, it's surely a bug in the
|
||||||
it's surely a bug in the code somewhere. Otherwise, close the
|
code somewhere. Otherwise, close the event and retry to create
|
||||||
event and retry to create an event with another name. */
|
a new event with another name. */
|
||||||
if (open_lock_obj ())
|
if (open_lock_obj ())
|
||||||
{
|
{
|
||||||
if (!IsEventSignalled (lf_obj))
|
if (!IsEventSignalled (lf_obj))
|
||||||
api_fatal ("NtCreateEvent(lock): %p", status);
|
api_fatal ("NtCreateEvent(lock): %p", status);
|
||||||
close_lock_obj ();
|
close_lock_obj ();
|
||||||
/* Change the lf_wid member to generate another name for the
|
/* Increment the lf_ver field until we have no collision. */
|
||||||
event, so as not to colide with the still-in-use event.
|
++lf_ver;
|
||||||
Changing lf_wid is ok, because the Windows PID is not used
|
|
||||||
for synchronization in the F_FLOCK case.
|
|
||||||
What we do here is to increment the highest byte in lf_wid. */
|
|
||||||
lf_wid = ((lf_wid & 0xff000000) + (1 << 24))
|
|
||||||
| (lf_wid & 0xffffff);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -836,7 +851,7 @@ restart: /* Entry point after a restartable signal came in. */
|
||||||
clean = new lockf_t ();
|
clean = new lockf_t ();
|
||||||
if (!clean)
|
if (!clean)
|
||||||
{
|
{
|
||||||
node->UNLOCK ();
|
node->unlock_and_remove ();
|
||||||
set_errno (ENOLCK);
|
set_errno (ENOLCK);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -847,10 +862,10 @@ restart: /* Entry point after a restartable signal came in. */
|
||||||
lockf_t *lock = new lockf_t (node, head, a_flags, type, start, end,
|
lockf_t *lock = new lockf_t (node, head, a_flags, type, start, end,
|
||||||
(a_flags & F_FLOCK) ? get_unique_id ()
|
(a_flags & F_FLOCK) ? get_unique_id ()
|
||||||
: getpid (),
|
: getpid (),
|
||||||
myself->dwProcessId);
|
myself->dwProcessId, 0);
|
||||||
if (!lock)
|
if (!lock)
|
||||||
{
|
{
|
||||||
node->UNLOCK ();
|
node->unlock_and_remove ();
|
||||||
set_errno (ENOLCK);
|
set_errno (ENOLCK);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -886,16 +901,7 @@ restart: /* Entry point after a restartable signal came in. */
|
||||||
delete lock;
|
delete lock;
|
||||||
lock = n;
|
lock = n;
|
||||||
}
|
}
|
||||||
if (node->i_lockf == NULL && !node->waiting ())
|
node->unlock_and_remove ();
|
||||||
{
|
|
||||||
INODE_LIST_LOCK ();
|
|
||||||
LIST_REMOVE (node, i_next);
|
|
||||||
node->UNLOCK ();
|
|
||||||
delete node;
|
|
||||||
INODE_LIST_UNLOCK ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
node->UNLOCK ();
|
|
||||||
switch (error)
|
switch (error)
|
||||||
{
|
{
|
||||||
case 0: /* All is well. */
|
case 0: /* All is well. */
|
||||||
|
@ -940,7 +946,6 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
node->i_all_lf = (lockf_t *) (void *) tp.w_get ();
|
node->i_all_lf = (lockf_t *) (void *) tp.w_get ();
|
||||||
while ((block = lf_getblock(lock, node)))
|
while ((block = lf_getblock(lock, node)))
|
||||||
{
|
{
|
||||||
DWORD ret;
|
|
||||||
HANDLE obj = block->lf_obj;
|
HANDLE obj = block->lf_obj;
|
||||||
block->lf_obj = NULL;
|
block->lf_obj = NULL;
|
||||||
|
|
||||||
|
@ -949,10 +954,9 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
*/
|
*/
|
||||||
if ((lock->lf_flags & F_WAIT) == 0)
|
if ((lock->lf_flags & F_WAIT) == 0)
|
||||||
{
|
{
|
||||||
|
NtClose (obj);
|
||||||
lock->lf_next = *clean;
|
lock->lf_next = *clean;
|
||||||
*clean = lock;
|
*clean = lock;
|
||||||
if (obj)
|
|
||||||
NtClose (obj);
|
|
||||||
return EAGAIN;
|
return EAGAIN;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -970,10 +974,10 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
waiting for one of our locks. This method isn't overly
|
waiting for one of our locks. This method isn't overly
|
||||||
intelligent. If it turns out to be too dumb, we might
|
intelligent. If it turns out to be too dumb, we might
|
||||||
have to remove it or to find another method. */
|
have to remove it or to find another method. */
|
||||||
|
if (lock->lf_flags & F_POSIX)
|
||||||
for (lockf_t *lk = node->i_lockf; lk; lk = lk->lf_next)
|
for (lockf_t *lk = node->i_lockf; lk; lk = lk->lf_next)
|
||||||
if ((lk->lf_flags & F_POSIX) && get_obj_handle_count (lk->lf_obj) > 1)
|
if ((lk->lf_flags & F_POSIX) && get_obj_handle_count (lk->lf_obj) > 1)
|
||||||
{
|
{
|
||||||
if (obj)
|
|
||||||
NtClose (obj);
|
NtClose (obj);
|
||||||
return EDEADLK;
|
return EDEADLK;
|
||||||
}
|
}
|
||||||
|
@ -996,76 +1000,50 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
*/
|
*/
|
||||||
/* Cygwin: No locked list. See deadlock recognition above. */
|
/* Cygwin: No locked list. See deadlock recognition above. */
|
||||||
|
|
||||||
/* Wait for the blocking object and its holding process. */
|
node->UNLOCK ();
|
||||||
if (!obj)
|
|
||||||
{
|
|
||||||
/* We can't synchronize on the lock event object.
|
|
||||||
Treat this as a deadlock-like situation for now. */
|
|
||||||
system_printf ("Can't sync with lock object hold by "
|
|
||||||
"Win32 pid %lu: %E", block->lf_wid);
|
|
||||||
return EDEADLK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE cancel_event = pthread::get_cancel_event ();
|
/* Create list of objects to wait for. */
|
||||||
|
HANDLE w4[4] = { obj, NULL, NULL, NULL };
|
||||||
|
DWORD wait_count = 1;
|
||||||
|
|
||||||
int wait_count = 0;
|
HANDLE proc = NULL;
|
||||||
/* The lock event is always the first object, all other wait objects
|
|
||||||
are variable. */
|
|
||||||
DWORD WAIT_SIGNAL_ARRIVED = WAIT_TIMEOUT + 1;
|
|
||||||
DWORD WAIT_THREAD_CANCELED = WAIT_TIMEOUT + 1;
|
|
||||||
|
|
||||||
SetThreadPriority (GetCurrentThread (), priority);
|
|
||||||
if (lock->lf_flags & F_POSIX)
|
if (lock->lf_flags & F_POSIX)
|
||||||
{
|
{
|
||||||
HANDLE proc = OpenProcess (SYNCHRONIZE, FALSE, block->lf_wid);
|
proc = OpenProcess (SYNCHRONIZE, FALSE, block->lf_wid);
|
||||||
if (!proc)
|
if (!proc)
|
||||||
{
|
debug_printf ("Can't sync with process holding a POSIX lock "
|
||||||
/* If we can't synchronize on the process holding the lock,
|
|
||||||
we will never recognize when the lock has been abandoned.
|
|
||||||
Treat this as a deadlock-like situation for now. */
|
|
||||||
system_printf ("Can't sync with process holding a lock "
|
|
||||||
"(Win32 pid %lu): %E", block->lf_wid);
|
"(Win32 pid %lu): %E", block->lf_wid);
|
||||||
NtClose (obj);
|
|
||||||
return EDEADLK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE w4[4] = { obj, proc, signal_arrived, cancel_event };
|
|
||||||
wait_count = 3;
|
|
||||||
WAIT_SIGNAL_ARRIVED = WAIT_OBJECT_0 + 2;
|
|
||||||
if (cancel_event)
|
|
||||||
{
|
|
||||||
wait_count = 4;
|
|
||||||
WAIT_THREAD_CANCELED = WAIT_OBJECT_0 + 3;
|
|
||||||
}
|
|
||||||
node->wait ();
|
|
||||||
node->UNLOCK ();
|
|
||||||
ret = WaitForMultipleObjects (wait_count, w4, FALSE, INFINITE);
|
|
||||||
CloseHandle (proc);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
w4[wait_count++] = proc;
|
||||||
HANDLE w4[3] = { obj, signal_arrived, cancel_event };
|
}
|
||||||
wait_count = 2;
|
DWORD WAIT_SIGNAL_ARRIVED = WAIT_OBJECT_0 + wait_count;
|
||||||
WAIT_SIGNAL_ARRIVED = WAIT_OBJECT_0 + 1;
|
w4[wait_count++] = signal_arrived;
|
||||||
|
|
||||||
|
DWORD WAIT_THREAD_CANCELED = WAIT_TIMEOUT + 1;
|
||||||
|
HANDLE cancel_event = pthread::get_cancel_event ();
|
||||||
if (cancel_event)
|
if (cancel_event)
|
||||||
{
|
{
|
||||||
wait_count = 3;
|
WAIT_THREAD_CANCELED = WAIT_OBJECT_0 + wait_count;
|
||||||
WAIT_THREAD_CANCELED = WAIT_OBJECT_0 + 2;
|
w4[wait_count++] = cancel_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
node->wait ();
|
/* Wait for the blocking object and, for POSIX locks, its holding process.
|
||||||
node->UNLOCK ();
|
Unfortunately, since BSD flock locks are not attached to a specific
|
||||||
/* Unfortunately, since BSD flock locks are not attached to a
|
process, we can't recognize an abandoned lock by sync'ing with the
|
||||||
specific process, we can't recognize an abandoned lock by
|
creator process. We have to make sure the event object is in a
|
||||||
sync'ing with a process. We have to make sure we're the only
|
signalled state, or that it has gone away. The latter we can only
|
||||||
process left accessing this event object, or that the event
|
recognize by retrying to fetch the block list, so we must not wait
|
||||||
object is in a signalled state. */
|
infinitely. Same problem for POSIX locks if the process has already
|
||||||
ret = WaitForMultipleObjects (wait_count, w4, FALSE, 100L);
|
exited at the time we're trying to open the process. */
|
||||||
}
|
SetThreadPriority (GetCurrentThread (), priority);
|
||||||
node->LOCK ();
|
DWORD ret = WaitForMultipleObjects (wait_count, w4, FALSE,
|
||||||
node->unwait ();
|
proc ? INFINITE : 100L);
|
||||||
NtClose (obj);
|
|
||||||
SetThreadPriority (GetCurrentThread (), old_prio);
|
SetThreadPriority (GetCurrentThread (), old_prio);
|
||||||
|
/* Always close handles before locking the node. */
|
||||||
|
NtClose (obj);
|
||||||
|
if (proc)
|
||||||
|
CloseHandle (proc);
|
||||||
|
node->LOCK ();
|
||||||
if (ret == WAIT_SIGNAL_ARRIVED)
|
if (ret == WAIT_SIGNAL_ARRIVED)
|
||||||
{
|
{
|
||||||
/* A signal came in. */
|
/* A signal came in. */
|
||||||
|
@ -1083,7 +1061,7 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
else
|
else
|
||||||
/* The lock object has been set to signalled or ...
|
/* The lock object has been set to signalled or ...
|
||||||
for POSIX locks, the process holding the lock has exited, or ...
|
for POSIX locks, the process holding the lock has exited, or ...
|
||||||
Just a timeout. Just retry. */
|
just a timeout. Just retry. */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
allow_others_to_sync ();
|
allow_others_to_sync ();
|
||||||
|
|
Loading…
Reference in New Issue