Commit c68cf93c authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent f7a8b297
...@@ -258,6 +258,15 @@ static global<error> errConnClosed = errors::New("connection closed"); ...@@ -258,6 +258,15 @@ static global<error> errConnClosed = errors::New("connection closed");
error _Conn::close() { error _Conn::close() {
_Conn& wconn = *this; _Conn& wconn = *this;
// lock virtmem early. TODO more granular virtmem locking (see __pin1 for
// details and why virt_lock currently goes first)
virt_lock();
bool virtUnlocked = false;
defer([&]() {
if (!virtUnlocked)
virt_unlock();
});
wconn._atMu.RLock(); wconn._atMu.RLock();
defer([&]() { defer([&]() {
wconn._atMu.RUnlock(); wconn._atMu.RUnlock();
...@@ -272,6 +281,7 @@ error _Conn::close() { ...@@ -272,6 +281,7 @@ error _Conn::close() {
eret = err; eret = err;
}; };
// mark wconn as closed, so that no new wconn.open might be spawned.
bool alreadyClosed = false; bool alreadyClosed = false;
wconn._filehMu.Lock(); wconn._filehMu.Lock();
alreadyClosed = (wconn._downErr == errConnClosed); alreadyClosed = (wconn._downErr == errConnClosed);
...@@ -280,39 +290,54 @@ error _Conn::close() { ...@@ -280,39 +290,54 @@ error _Conn::close() {
if (alreadyClosed) if (alreadyClosed)
return nil; return nil;
// close wlink and signal to pinner to stop outside of wconn.filehMu // close all files - both that have no mappings and that still have opened
err = wconn._wlink->close(); // XXX ok under atMu? // mappings. We have to close files before shutting down pinner, because
if (err != nil) // wcfs might send pin messages due to file access by other clients. So to
reterr1(err); // avoid being killed we have to unwatch all files before stopping the
wconn._pinCancel(); // pinner.
err = wconn._pinWG->wait(); // XXX ok under atMu?
if (!errors::Is(err, context::canceled)) // canceled - ok
reterr1(err);
// pinner is stopped - now close all files - both that have no mappings and
// that still have opened mappings.
// //
// NOTE after file is closed mappings could continue to survive, but we can no // NOTE after file is closed, its mappings could continue to survive, but
// longer maintain consistent view. For this reason we change mappings to // we can no longer maintain consistent view. For this reason we change
// something that gives EFAULT on access. XXX implement // mappings to give EFAULT on access.
while (1) {
FileH f = nil;
// pick up any fileh
wconn._filehMu.Lock(); wconn._filehMu.Lock();
defer([&]() { if (!wconn._filehTab.empty())
f = wconn._filehTab.begin()->second;
wconn._filehMu.Unlock(); wconn._filehMu.Unlock();
});
// XXX f locking if (f == nil)
for (auto _ : wconn._filehTab) { break; // all closed
//zodb::Oid fid = _.first;
FileH f = _.second; // XXX vvv only for !opening state
err = f->_headf->close(); // XXX mark fileh as down so that fileh.close does not say "bad fd"
// close f under
// - XXX virt_lock
// - wconn.atMu.R
// - wconn.filehMu unlocked
err = f->_closeLocked();
if (err != nil) if (err != nil)
reterr1(err); reterr1(err);
f->_headf = nil;
// XXX stop watching f XXX ok under mu? // wait for f close to complete, as it might be that f.close was called
// simultaneously to wconn.close
f->_closedq.recv();
} }
wconn._filehTab.clear(); // close wlink and signal to pinner to stop.
// we have to release virt_lock, to avoid deadlocking with pinner.
virtUnlocked = true;
virt_unlock();
err = wconn._wlink->close();
if (err != nil)
reterr1(err);
wconn._pinCancel();
err = wconn._pinWG->wait();
if (!errors::Is(err, context::canceled)) // canceled - ok
reterr1(err);
return E(eret); return E(eret);
} }
...@@ -682,10 +707,10 @@ pair<FileH, error> _Conn::open(zodb::Oid foid) { ...@@ -682,10 +707,10 @@ pair<FileH, error> _Conn::open(zodb::Oid foid) {
return make_pair(nil, E(err)); return make_pair(nil, E(err));
} }
// XXX ensure f<foid> @ wconn.at exists? // XXX ensure f<foid>@ wconn.at exists?
// XXX else we get pins to non-exists state from wcfs. // XXX else we get pins to non-exists state from wcfs.
// //
// XXX -> better teach wcfs to reject "watch <foid> @at" for @at where foid did not existed. // XXX -> better teach wcfs to reject "watch <foid> @at" for @at where f did not existed.
retry: retry:
FileH f; bool ok; FileH f; bool ok;
...@@ -758,6 +783,8 @@ retry: ...@@ -758,6 +783,8 @@ retry:
if (f->_openErr != nil) if (f->_openErr != nil)
return make_pair(nil, E(f->_openErr)); return make_pair(nil, E(f->_openErr));
// XXX recheck the wconn was not closed while the open was in progress
retok = true; retok = true;
return make_pair(f, nil); return make_pair(f, nil);
} }
...@@ -816,7 +843,7 @@ error _FileH::close() { ...@@ -816,7 +843,7 @@ error _FileH::close() {
_FileH& fileh = *this; _FileH& fileh = *this;
Conn wconn = fileh.wconn; Conn wconn = fileh.wconn;
// lock virtmem first. TODO more granular virtmem locking (see __pin1 for // lock virtmem early. TODO more granular virtmem locking (see __pin1 for
// details and why virt_lock currently goes first) // details and why virt_lock currently goes first)
virt_lock(); virt_lock();
defer([&]() { defer([&]() {
...@@ -824,10 +851,25 @@ error _FileH::close() { ...@@ -824,10 +851,25 @@ error _FileH::close() {
}); });
wconn->_atMu.RLock(); wconn->_atMu.RLock();
defer([&]() {
wconn->_atMu.RUnlock();
});
return fileh._closeLocked();
}
// _closeLocked serves FileH.close and Conn.close.
//
// Must be called with the following locks held by caller:
// - virt_lock
// - wconn.atMu
error _FileH::_closeLocked() {
_FileH& fileh = *this;
Conn wconn = fileh.wconn;
wconn->_filehMu.Lock(); wconn->_filehMu.Lock();
defer([&]() { defer([&]() {
wconn->_filehMu.Unlock(); wconn->_filehMu.Unlock();
wconn->_atMu.RUnlock();
}); });
// fileh.close can be called several times. just return nil for second close. // fileh.close can be called several times. just return nil for second close.
......
...@@ -261,9 +261,10 @@ public: ...@@ -261,9 +261,10 @@ public:
public: public:
error close(); error close();
pair<Mapping, error> mmap(int64_t blk_start, int64_t blk_len, VMA *vma=nil); pair<Mapping, error> mmap(int64_t blk_start, int64_t blk_len, VMA *vma=nil);
string String() const;
error _open(); error _open();
string String() const; error _closeLocked();
}; };
// Mapping represents one memory mapping of FileH. // Mapping represents one memory mapping of FileH.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment