retry flock() if interrupted (EINTR)
[folly.git] / folly / File.cpp
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "folly/File.h"
18
19 #include <fcntl.h>
20 #include <unistd.h>
21
22 #include "folly/Exception.h"
23 #include "folly/FileUtil.h"
24 #include "folly/Format.h"
25 #include "folly/ScopeGuard.h"
26
27 #include <system_error>
28
29 #include <glog/logging.h>
30
31 namespace folly {
32
33 File::File()
34   : fd_(-1)
35   , ownsFd_(false)
36 {}
37
38 File::File(int fd, bool ownsFd)
39   : fd_(fd)
40   , ownsFd_(ownsFd)
41 {}
42
43 File::File(const char* name, int flags, mode_t mode)
44   : fd_(::open(name, flags, mode))
45   , ownsFd_(false) {
46   if (fd_ == -1) {
47     throwSystemError(folly::format("open(\"{}\", {:#o}, 0{:#o}) failed",
48                                    name, flags, mode).fbstr());
49   }
50   ownsFd_ = true;
51 }
52
53 File::File(File&& other)
54   : fd_(other.fd_)
55   , ownsFd_(other.ownsFd_) {
56
57   other.release();
58 }
59
60 File& File::operator=(File&& other) {
61   closeNoThrow();
62   swap(other);
63   return *this;
64 }
65
66 File::~File() {
67   closeNoThrow();  // ignore error
68 }
69
70 /* static */ File File::temporary() {
71   // make a temp file with tmpfile(), dup the fd, then return it in a File.
72   FILE* tmpFile = tmpfile();
73   checkFopenError(tmpFile, "tmpfile() failed");
74   SCOPE_EXIT { fclose(tmpFile); };
75
76   int fd = dup(fileno(tmpFile));
77   checkUnixError(fd, "dup() failed");
78
79   return File(fd, true);
80 }
81
82 void File::release() {
83   fd_ = -1;
84   ownsFd_ = false;
85 }
86
87 void File::swap(File& other) {
88   using std::swap;
89   swap(fd_, other.fd_);
90   swap(ownsFd_, other.ownsFd_);
91 }
92
93 void swap(File& a, File& b) {
94   a.swap(b);
95 }
96
97 void File::close() {
98   if (!closeNoThrow()) {
99     throwSystemError("close() failed");
100   }
101 }
102
103 bool File::closeNoThrow() {
104   int r = ownsFd_ ? ::close(fd_) : 0;
105   release();
106   return r == 0;
107 }
108
109 void File::lock() { doLock(LOCK_EX); }
110 bool File::try_lock() { return doTryLock(LOCK_EX); }
111 void File::lock_shared() { doLock(LOCK_SH); }
112 bool File::try_lock_shared() { return doTryLock(LOCK_SH); }
113
114 void File::doLock(int op) {
115   checkUnixError(flockNoInt(fd_, op), "flock() failed (lock)");
116 }
117
118 bool File::doTryLock(int op) {
119   int r = flockNoInt(fd_, op | LOCK_NB);
120   // flock returns EWOULDBLOCK if already locked
121   if (r == -1 && errno == EWOULDBLOCK) return false;
122   checkUnixError(r, "flock() failed (try_lock)");
123   return true;
124 }
125
126 void File::unlock() {
127   checkUnixError(flockNoInt(fd_, LOCK_UN), "flock() failed (unlock)");
128 }
129 void File::unlock_shared() { unlock(); }
130
131 }  // namespace folly