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