1 : /*
2 : * Copyright (c) 2013 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <errno.h>
8 : #include <string.h>
9 : #include <sys/file.h>
10 :
11 : #include "native_client/src/shared/platform/posix/nacl_file_lock_intern.h"
12 :
13 : #include "native_client/src/shared/platform/nacl_check.h"
14 : #include "native_client/src/shared/platform/nacl_host_desc.h"
15 : #include "native_client/src/shared/platform/nacl_log.h"
16 : #include "native_client/src/shared/platform/nacl_sync.h"
17 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
18 :
19 :
20 : static struct NaClFileLockEntry **NaClFileLockManagerFindEntryMu(
21 60 : struct NaClFileLockManager *self,
22 60 : struct NaClFileLockEntry const *key) {
23 60 : struct NaClFileLockEntry **pptr;
24 60 : struct NaClFileLockEntry *ptr;
25 :
26 164 : for (pptr = &self->head; NULL != (ptr = *pptr); pptr = &ptr->next) {
27 136 : if (ptr->file_dev == key->file_dev &&
28 : ptr->file_ino == key->file_ino) {
29 46 : return pptr;
30 : }
31 22 : }
32 14 : return NULL;
33 60 : }
34 :
35 : static struct NaClFileLockEntry *NaClFileLockManagerEntryFactory(
36 14 : struct NaClFileLockManager *self,
37 14 : int desc) {
38 14 : struct NaClFileLockEntry *result;
39 :
40 14 : result = malloc(sizeof *result);
41 42 : CHECK(NULL != result);
42 14 : (*self->set_file_identity_data)(result, desc);
43 14 : result->next = NULL;
44 14 : NaClXMutexCtor(&result->mu);
45 14 : NaClXCondVarCtor(&result->cv);
46 14 : result->holding_lock = 1; /* caller is creating to hold the lock */
47 14 : result->num_waiting = 0;
48 14 : return result;
49 : }
50 :
51 : static void NaClFileLockManagerFileEntryRecycler(
52 14 : struct NaClFileLockEntry **entryp) {
53 14 : struct NaClFileLockEntry *entry;
54 42 : CHECK(NULL != entryp);
55 14 : entry = *entryp;
56 42 : CHECK(0 == entry->holding_lock);
57 42 : CHECK(0 == entry->num_waiting);
58 14 : entry->file_dev = 0;
59 14 : entry->file_ino = 0;
60 14 : entry->next = NULL;
61 14 : NaClMutexDtor(&entry->mu);
62 14 : NaClCondVarDtor(&entry->cv);
63 14 : entry->holding_lock = 0;
64 14 : entry->num_waiting = 0;
65 14 : free(entry);
66 14 : *entryp = NULL;
67 14 : }
68 :
69 : static void NaClFileLockManagerSetFileIdentityData(
70 37 : struct NaClFileLockEntry *entry,
71 37 : int desc) {
72 37 : nacl_host_stat_t stbuf;
73 37 : if (0 != NACL_HOST_FSTAT64(desc, &stbuf)) {
74 0 : NaClLog(LOG_FATAL,
75 : "NaClFileLockManagerSetFileIdentityData: fstat failed, desc %d,"
76 0 : " errno %d\n", desc, errno);
77 0 : }
78 37 : entry->file_dev = stbuf.st_dev;
79 37 : entry->file_ino = stbuf.st_ino;
80 37 : }
81 :
82 15 : static void NaClFileLockManagerTakeLock(int desc) {
83 15 : if (0 != flock(desc, LOCK_EX)) {
84 0 : char buf[256];
85 0 : int err = errno;
86 0 : (void) strerror_r(err, buf, sizeof buf);
87 0 : NaClLog(LOG_FATAL,
88 : "NaClFileLockManagerTakeLock: flock failed: %s (%d)\n",
89 : buf, err);
90 0 : }
91 15 : }
92 :
93 15 : static void NaClFileLockManagerDropLock(int desc) {
94 15 : if (0 != flock(desc, LOCK_UN)) {
95 0 : char buf[256];
96 0 : int err = errno;
97 0 : (void) strerror_r(err, buf, sizeof buf);
98 0 : NaClLog(LOG_FATAL,
99 : "NaClFileLockManagerDropLock: flock failed: %s (%d)\n",
100 : buf, err);
101 0 : }
102 15 : }
103 :
104 8 : void NaClFileLockManagerCtor(struct NaClFileLockManager *self) {
105 8 : NaClXMutexCtor(&self->mu);
106 8 : self->head = NULL;
107 8 : self->set_file_identity_data = NaClFileLockManagerSetFileIdentityData;
108 8 : self->take_file_lock = NaClFileLockManagerTakeLock;
109 8 : self->drop_file_lock = NaClFileLockManagerDropLock;
110 8 : }
111 :
112 8 : void NaClFileLockManagerDtor(struct NaClFileLockManager *self) {
113 24 : CHECK(NULL == self->head);
114 8 : NaClMutexDtor(&self->mu);
115 8 : }
116 :
117 30 : void NaClFileLockManagerLock(struct NaClFileLockManager *self,
118 30 : int desc) {
119 30 : struct NaClFileLockEntry key;
120 30 : struct NaClFileLockEntry **existing;
121 30 : struct NaClFileLockEntry *entry;
122 :
123 30 : (*self->set_file_identity_data)(&key, desc);
124 :
125 30 : NaClXMutexLock(&self->mu);
126 30 : existing = NaClFileLockManagerFindEntryMu(self, &key);
127 30 : if (NULL == existing) {
128 : /* make new entry */
129 14 : entry = NaClFileLockManagerEntryFactory(self, desc);
130 14 : entry->next = self->head;
131 14 : self->head = entry;
132 14 : NaClXMutexUnlock(&self->mu);
133 14 : } else {
134 16 : entry = *existing;
135 16 : NaClXMutexLock(&entry->mu);
136 16 : entry->num_waiting++;
137 : /* arithmetic overflow */
138 48 : CHECK(0 != entry->num_waiting);
139 : /* drop container lock after ensuring that the entry will not be deleted */
140 16 : NaClXMutexUnlock(&self->mu);
141 52 : while (entry->holding_lock) {
142 20 : NaClXCondVarWait(&entry->cv, &entry->mu);
143 20 : }
144 16 : entry->holding_lock = 1;
145 16 : entry->num_waiting--;
146 16 : NaClXMutexUnlock(&entry->mu);
147 : }
148 30 : (*self->take_file_lock)(desc);
149 30 : }
150 :
151 30 : void NaClFileLockManagerUnlock(struct NaClFileLockManager *self,
152 30 : int desc) {
153 30 : struct NaClFileLockEntry key;
154 30 : struct NaClFileLockEntry **existing;
155 30 : struct NaClFileLockEntry *entry;
156 :
157 30 : (*self->set_file_identity_data)(&key, desc);
158 :
159 30 : NaClXMutexLock(&self->mu);
160 30 : existing = NaClFileLockManagerFindEntryMu(self, &key);
161 90 : CHECK(NULL != existing);
162 30 : entry = *existing;
163 30 : NaClXMutexLock(&entry->mu);
164 30 : entry->holding_lock = 0;
165 30 : if (0 == entry->num_waiting) {
166 14 : *existing = entry->next;
167 14 : NaClXMutexUnlock(&entry->mu);
168 14 : NaClXMutexUnlock(&self->mu);
169 14 : NaClFileLockManagerFileEntryRecycler(&entry);
170 14 : } else {
171 16 : NaClXMutexUnlock(&self->mu);
172 : /* tell waiting threads that they can now compete for the lock */
173 16 : NaClXCondVarBroadcast(&entry->cv);
174 16 : NaClXMutexUnlock(&entry->mu);
175 : }
176 30 : (*self->drop_file_lock)(desc);
177 30 : }
|