first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / library / processes / rendezvous.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : rendezvous                                                        *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2001-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 //note: after repeated investigation, it seems that if we unlink the rendezvous
16 //      file on destruction, then this hoses up any locks attempted ever after.
17 //      instead of waiting for a lock, new attempts think they can go ahead,
18 //      even though someone else might also have been given the lock.  it seems
19 //      we cannot remove the files without destroying the semantics.
20
21 #include "rendezvous.h"
22
23 #include <application/windoze_helper.h>
24 #include <basis/astring.h>
25 #include <basis/functions.h>
26 #include <basis/utf_conversion.h>
27 #include <filesystem/filename.h>
28
29 #ifdef __UNIX__
30   #include <stdio.h>
31   #include <sys/stat.h>
32   #include <unistd.h>
33 #endif
34
35 using namespace basis;
36 using namespace filesystem;
37
38 namespace processes {
39
40 //#define DEBUG_RENDEZVOUS
41   // uncomment for a noisier file.
42
43 #undef LOG
44 #define LOG(tp) printf("%s%s\n", time_stamp::notarize(true).s(), astring(tp).s())
45   // we can only use simple logging here since the rendezvous is relied on
46   // at very low levels and use of a log_base object would cause infinite
47   // loops.
48
49 // used for the name of a mutex or part of the unix lock filename.
50 astring general_lock_name(const astring &root_part)
51 { return root_part + "_app_lock"; }
52
53 #ifdef __UNIX__
54 // the name of the locking file used in unix.
55 astring unix_rendez_file(const astring &lock_name)
56 {
57   astring clean_name = lock_name;
58   // remove troublesome characters from the name.
59   filename::detooth_filename(clean_name);
60   // make sure our target directory exists.
61
62   // this choice is only user specific.
63 //  astring tmp_dir = portable::env_string("TMP") + "/rendezvous";
64
65   // this choice uses a system-wide location.
66   astring tmp_dir = "/tmp/rendezvous";
67
68   mkdir(tmp_dir.observe(), 0777);
69   return tmp_dir + "/ren_" + clean_name;
70 }
71 #endif
72
73 rendezvous::rendezvous(const astring &root_name)
74 : _handle(NIL),
75   _locked(false),
76   _root_name(new astring(root_name))
77 {
78 #ifdef DEBUG_RENDEZVOUS
79   FUNCDEF("constructor");
80 #endif
81   astring lock_name = general_lock_name(root_name);
82 #ifdef __UNIX__
83   astring real_name = unix_rendez_file(lock_name);
84   FILE *locking_file = fopen(real_name.s(), "wb");
85   if (!locking_file) {
86 #ifdef DEBUG_RENDEZVOUS
87     LOG(astring("failure to create locking file ") + real_name
88         + ": " + critical_events::system_error_text(critical_events::system_error()) );
89 #endif
90     return;
91   }
92   // success now.
93   _handle = locking_file;
94 #endif
95 #ifdef __WIN32__
96   _handle = CreateMutex(NIL, false, to_unicode_temp(lock_name));
97   if (!_handle) return;
98 #endif
99 }
100
101 rendezvous::~rendezvous()
102 {
103 #ifdef DEBUG_RENDEZVOUS
104   FUNCDEF("destructor");
105   LOG("okay, into destructor for rendezvous.");
106 #endif
107 #ifdef __UNIX__
108   if (_handle) {
109     if (_locked) {
110       int ret = lockf(fileno((FILE *)_handle), F_ULOCK, sizeof(int));
111       if (ret) {
112 #ifdef DEBUG_RENDEZVOUS
113         LOG("failure to get lock on file.");
114 #endif
115       }
116       _locked = false;  // clear our locked status since we no longer have one.
117
118 //note: after repeated investigation, it seems that if we unlink the rendezvous
119 //      file on destruction, then this hoses up any locks attempted ever after.
120 //      instead of waiting for a lock, new attempts think they can go ahead,
121 //      even though someone else might also have been given the lock.  it seems
122 //      we cannot remove the files without destroying the semantics.
123     }
124
125     fclose((FILE *)_handle);
126     _handle = NIL;
127   }
128 #endif
129 #ifdef __WIN32__
130   if (_handle) CloseHandle((HANDLE)_handle);
131 #endif
132   WHACK(_root_name);
133 }
134
135 void rendezvous::establish_lock() { lock(); }
136
137 void rendezvous::repeal_lock() { unlock(); }
138
139 bool rendezvous::healthy() const
140 {
141   return !!_handle;
142 }
143
144 bool rendezvous::lock(locking_methods how)
145 {
146 #ifdef DEBUG_RENDEZVOUS
147   FUNCDEF("lock");
148 #endif
149   if (how == NO_LOCKING) return false;
150   if (!healthy()) return false;
151 #ifdef __UNIX__
152   int command = F_TLOCK;
153   if (how == ENDLESS_WAIT) command = F_LOCK;
154   int ret = lockf(fileno((FILE *)_handle), command, sizeof(int));
155   if (ret) {
156 #ifdef DEBUG_RENDEZVOUS
157     LOG("failure to get lock on file.");
158 #endif
159     return false;
160   }
161 #ifdef DEBUG_RENDEZVOUS
162   LOG("okay, got lock on shared mem.");
163 #endif
164   _locked = true;
165   return true;
166 #endif
167 #ifdef __WIN32__
168   int timing = 0;  // immediate return.
169   if (how == ENDLESS_WAIT) timing = INFINITE;
170   int ret = WaitForSingleObject((HANDLE)_handle, timing);
171   if ( (ret == WAIT_ABANDONED) || (ret == WAIT_TIMEOUT) ) return false;
172   else if (ret != WAIT_OBJECT_0) {
173 #ifdef DEBUG_RENDEZVOUS
174     LOG("got an unanticipated error from waiting for the mutex.");
175 #endif
176     return false;
177   }
178   _locked = true;
179   return true;
180 #endif
181   return false;
182 }
183
184 void rendezvous::unlock()
185 {
186 #ifdef DEBUG_RENDEZVOUS
187   FUNCDEF("unlock");
188 #endif
189   if (!healthy()) return;
190   if (_locked) {
191 #ifdef __UNIX__
192     int ret = lockf(fileno((FILE *)_handle), F_ULOCK, sizeof(int));
193     if (ret) {
194 #ifdef DEBUG_RENDEZVOUS
195       LOG("failure to get lock on file.");
196 #endif
197     }
198 #endif
199 #ifdef __WIN32__
200     ReleaseMutex((HANDLE)_handle);
201 #endif
202     _locked = false;
203   } else {
204 #ifdef DEBUG_RENDEZVOUS
205     LOG("okay, rendezvous wasn't locked.");
206 #endif
207   }
208 }
209
210 } //namespace.
211