1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include <dirent.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <fnmatch.h>
20 #include <stdio.h>
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <time.h>
26 #include <unistd.h>
27
28 #include <thread>
29 #include <vector>
30
31 #include "tensorflow/core/lib/core/error_codes.pb.h"
32 #include "tensorflow/core/platform/env.h"
33 #include "tensorflow/core/platform/load_library.h"
34 #include "tensorflow/core/platform/logging.h"
35 #include "tensorflow/core/platform/posix/posix_file_system.h"
36
37 namespace tensorflow {
38
39 namespace {
40
41 class StdThread : public Thread {
42 public:
43 // name and thread_options are both ignored.
StdThread(const ThreadOptions & thread_options,const string & name,std::function<void ()> fn)44 StdThread(const ThreadOptions& thread_options, const string& name,
45 std::function<void()> fn)
46 : thread_(fn) {}
~StdThread()47 ~StdThread() override { thread_.join(); }
48
49 private:
50 std::thread thread_;
51 };
52
53 class PosixEnv : public Env {
54 public:
PosixEnv()55 PosixEnv() {}
56
~PosixEnv()57 ~PosixEnv() override { LOG(FATAL) << "Env::Default() must not be destroyed"; }
58
MatchPath(const string & path,const string & pattern)59 bool MatchPath(const string& path, const string& pattern) override {
60 return fnmatch(pattern.c_str(), path.c_str(), FNM_PATHNAME) == 0;
61 }
62
SleepForMicroseconds(int64 micros)63 void SleepForMicroseconds(int64 micros) override {
64 while (micros > 0) {
65 timespec sleep_time;
66 sleep_time.tv_sec = 0;
67 sleep_time.tv_nsec = 0;
68
69 if (micros >= 1e6) {
70 sleep_time.tv_sec =
71 std::min<int64>(micros / 1e6, std::numeric_limits<time_t>::max());
72 micros -= static_cast<int64>(sleep_time.tv_sec) * 1e6;
73 }
74 if (micros < 1e6) {
75 sleep_time.tv_nsec = 1000 * micros;
76 micros = 0;
77 }
78 while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {
79 // Ignore signals and wait for the full interval to elapse.
80 }
81 }
82 }
83
StartThread(const ThreadOptions & thread_options,const string & name,std::function<void ()> fn)84 Thread* StartThread(const ThreadOptions& thread_options, const string& name,
85 std::function<void()> fn) override {
86 return new StdThread(thread_options, name, fn);
87 }
88
GetCurrentThreadId()89 int32 GetCurrentThreadId() override {
90 #ifdef __APPLE__
91 uint64_t tid64;
92 pthread_threadid_np(nullptr, &tid64);
93 return static_cast<int32>(tid64);
94 #elif defined(__FreeBSD__)
95 // Has to be casted to long first, else this error appears:
96 // static_cast from 'pthread_t' (aka 'pthread *') to 'int32' (aka 'int')
97 // is not allowed
98 return static_cast<int32>(static_cast<int64>(pthread_self()));
99 #else
100 return static_cast<int32>(pthread_self());
101 #endif
102 }
103
GetCurrentThreadName(string * name)104 bool GetCurrentThreadName(string* name) override {
105 #if defined(__ANDROID__) || defined(__EMSCRIPTEN__)
106 return false;
107 #else
108 char buf[100];
109 int res = pthread_getname_np(pthread_self(), buf, static_cast<size_t>(100));
110 if (res != 0) {
111 return false;
112 }
113 *name = buf;
114 return true;
115 #endif
116 }
117
SchedClosure(std::function<void ()> closure)118 void SchedClosure(std::function<void()> closure) override {
119 // TODO(b/27290852): Spawning a new thread here is wasteful, but
120 // needed to deal with the fact that many `closure` functions are
121 // blocking in the current codebase.
122 std::thread closure_thread(closure);
123 closure_thread.detach();
124 }
125
SchedClosureAfter(int64 micros,std::function<void ()> closure)126 void SchedClosureAfter(int64 micros, std::function<void()> closure) override {
127 // TODO(b/27290852): Consuming a thread here is wasteful, but this
128 // code is (currently) only used in the case where a step fails
129 // (AbortStep). This could be replaced by a timer thread
130 SchedClosure([this, micros, closure]() {
131 SleepForMicroseconds(micros);
132 closure();
133 });
134 }
135
LoadLibrary(const char * library_filename,void ** handle)136 Status LoadLibrary(const char* library_filename, void** handle) override {
137 return tensorflow::internal::LoadLibrary(library_filename, handle);
138 }
139
GetSymbolFromLibrary(void * handle,const char * symbol_name,void ** symbol)140 Status GetSymbolFromLibrary(void* handle, const char* symbol_name,
141 void** symbol) override {
142 return tensorflow::internal::GetSymbolFromLibrary(handle, symbol_name,
143 symbol);
144 }
145
FormatLibraryFileName(const string & name,const string & version)146 string FormatLibraryFileName(const string& name,
147 const string& version) override {
148 return tensorflow::internal::FormatLibraryFileName(name, version);
149 }
150
GetRunfilesDir()151 string GetRunfilesDir() override {
152 string bin_path = this->GetExecutablePath();
153 string runfiles_suffix = ".runfiles/org_tensorflow";
154 std::size_t pos = bin_path.find(runfiles_suffix);
155
156 // Sometimes (when executing under python) bin_path returns the full path to
157 // the python scripts under runfiles. Get the substring.
158 if (pos != std::string::npos) {
159 return bin_path.substr(0, pos + runfiles_suffix.length());
160 }
161
162 // See if we have the executable path. if executable.runfiles exists, return
163 // that folder.
164 string runfiles_path = bin_path + runfiles_suffix;
165 Status s = this->IsDirectory(runfiles_path);
166 if (s.ok()) {
167 return runfiles_path;
168 }
169
170 // If nothing can be found, return something close.
171 return bin_path.substr(0, bin_path.find_last_of("/\\"));
172 }
173
174 private:
175 void GetLocalTempDirectories(std::vector<string>* list) override;
176 };
177
178 } // namespace
179
180 #if defined(PLATFORM_POSIX) || defined(__ANDROID__)
181 REGISTER_FILE_SYSTEM("", PosixFileSystem);
182 REGISTER_FILE_SYSTEM("file", LocalPosixFileSystem);
Default()183 Env* Env::Default() {
184 static Env* default_env = new PosixEnv;
185 return default_env;
186 }
187 #endif
188
GetLocalTempDirectories(std::vector<string> * list)189 void PosixEnv::GetLocalTempDirectories(std::vector<string>* list) {
190 list->clear();
191 // Directories, in order of preference. If we find a dir that
192 // exists, we stop adding other less-preferred dirs
193 const char* candidates[] = {
194 // Non-null only during unittest/regtest
195 getenv("TEST_TMPDIR"),
196
197 // Explicitly-supplied temp dirs
198 getenv("TMPDIR"),
199 getenv("TMP"),
200
201 #if defined(__ANDROID__)
202 "/data/local/tmp",
203 #endif
204
205 // If all else fails
206 "/tmp",
207 };
208
209 for (const char* d : candidates) {
210 if (!d || d[0] == '\0') continue; // Empty env var
211
212 // Make sure we don't surprise anyone who's expecting a '/'
213 string dstr = d;
214 if (dstr[dstr.size() - 1] != '/') {
215 dstr += "/";
216 }
217
218 struct stat statbuf;
219 if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode) &&
220 !access(dstr.c_str(), 0)) {
221 // We found a dir that exists and is accessible - we're done.
222 list->push_back(dstr);
223 return;
224 }
225 }
226 }
227
228 } // namespace tensorflow
229