Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
W
wendelin.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Kirill Smelkov
wendelin.core
Commits
e6e6644a
Commit
e6e6644a
authored
Feb 19, 2020
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
efdf1420
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
105 additions
and
62 deletions
+105
-62
wcfs/client/wcfs.cpp
wcfs/client/wcfs.cpp
+92
-52
wcfs/client/wcfs.h
wcfs/client/wcfs.h
+13
-10
No files found.
wcfs/client/wcfs.cpp
View file @
e6e6644a
...
...
@@ -118,6 +118,12 @@ static global<error> errConnClosed = errors::New("connection closed");
// opened fileh and mappings become invalid to use except close and unmap.
error
_Conn
::
close
()
{
_Conn
&
wconn
=
*
this
;
wconn
.
_atMu
.
RLock
();
defer
([
&
]()
{
wconn
.
_atMu
.
RUnlock
();
});
xerr
::
Contextf
E
(
"wcfs %s: close conn @%s"
,
v
(
wconn
.
_wc
->
mountpoint
),
v
(
wconn
.
at
));
// XXX + conn # e.g. from wconn._wlink.id? or wlink.close should include its id itself?
// (or ._wlink._f.fd() ?)
...
...
@@ -128,15 +134,18 @@ error _Conn::close() {
eret
=
err
;
};
wconn
.
_downMu
.
lock
();
wconn
.
_mu
.
lock
();
defer
([
&
]()
{
wconn
.
_mu
.
unlock
();
});
wconn
.
_downErr
=
errConnClosed
;
// XXX ok to change even if it was !nil before?
wconn
.
_downMu
.
unlock
();
err
=
wconn
.
_wlink
->
close
();
err
=
wconn
.
_wlink
->
close
();
// XXX ok under mu?
if
(
err
!=
nil
)
reterr1
(
err
);
wconn
.
_pinCancel
();
err
=
wconn
.
_pinWG
->
wait
();
err
=
wconn
.
_pinWG
->
wait
();
// XXX ok under mu?
if
(
!
errors
::
Is
(
err
,
context
::
canceled
))
// canceled - ok
reterr1
(
err
);
...
...
@@ -145,10 +154,6 @@ error _Conn::close() {
// NOTE after file is closed mappings could continue to survive, but we can no
// longer maintain consistent view. For this reason we change mappings to
// something that gives EFAULT on access. XXX implement
wconn
.
_filehmu
.
lock
();
defer
([
&
]()
{
wconn
.
_filehmu
.
unlock
();
});
for
(
auto
_
:
wconn
.
_filehtab
)
{
auto
f
=
_
.
second
;
...
...
@@ -157,7 +162,7 @@ error _Conn::close() {
reterr1
(
err
);
f
->
_headf
=
nil
;
// XXX stop watching f
// XXX stop watching f
XXX ok under mu?
}
wconn
.
_filehtab
.
clear
();
...
...
@@ -182,12 +187,12 @@ error _Conn::_pinner(context::Context ctx) {
}
// mark the connection non-operational if the pinner fails
wconn
.
_
downMu
.
lock
();
wconn
.
_
mu
.
lock
();
// XXX locking ok? -> merge into below where lock is held? XXX + atMu.R ?
if
(
wconn
.
_downErr
==
nil
)
{
wconn
.
_downErr
=
fmt
::
errorf
(
"no longer operational due to: %w"
,
err
);
// XXX make all fileh and mapping invalid.
}
wconn
.
_
downM
u
.
unlock
();
wconn
.
_
m
u
.
unlock
();
return
err
;
}
...
...
@@ -204,9 +209,9 @@ error _Conn::__pinner(context::Context ctx) {
if
(
err
!=
nil
)
{
// it is ok if we receive EOF due to us closing the connection
if
(
err
==
io
::
EOF_
)
{
wconn
.
_
downM
u
.
lock
();
wconn
.
_
m
u
.
lock
();
err
=
(
wconn
.
_downErr
==
errConnClosed
)
?
nil
:
io
::
ErrUnexpectedEOF
;
wconn
.
_
downM
u
.
unlock
();
wconn
.
_
m
u
.
unlock
();
}
return
E
(
err
);
}
...
...
@@ -230,7 +235,8 @@ error _Conn::_pin1(PinReq *req) {
string
ack
=
"ack"
;
if
(
err
!=
nil
)
ack
=
fmt
::
sprintf
(
"nak: %s"
,
v
(
err
));
error
err2
=
wconn
.
_wlink
->
replyReq
(
context
::
background
(),
req
,
ack
);
// XXX ctx ok?
// NOTE ctx=bg to always send reply even if we are canceled
error
err2
=
wconn
.
_wlink
->
replyReq
(
context
::
background
(),
req
,
ack
);
if
(
err
==
nil
)
err
=
err2
;
...
...
@@ -242,22 +248,28 @@ error _Conn::__pin1(PinReq *req) {
FileH
f
;
bool
ok
;
wconn
.
_atMu
.
RLock
();
// XXX deadlock wrt Conn.open, Conn.resync ?
defer
([
&
]()
{
wconn
.
_atMu
.
RUnlock
();
});
// XXX deadlock wrt Conn.open which locks wconn.filehmu and starts initial "watch"
wconn
.
_
fileh
mu
.
lock
();
wconn
.
_mu
.
lock
();
// XXX +incref f, so that simultaneous close does not remove f from wconn.filehTab ?
// XXX or just make FileH.close lock f too to synchronizw with pinner?
tie
(
f
,
ok
)
=
wconn
.
_filehtab
.
get_
(
req
->
foid
);
if
(
!
ok
)
{
wconn
.
_
fileh
mu
.
unlock
();
wconn
.
_mu
.
unlock
();
// why wcfs sent us this update?
return
fmt
::
errorf
(
"unexpected pin: f<%s> not watched"
,
v
(
req
->
foid
));
}
// XXX relock wconn -> f
// wconn.mu.unlock()
// f.mu.lock()
//
// XXX (?) NOTE: _not_ taking wconn.atMu at all (think more / explain why)
// (e.g. deadlock with Conn.resync (wlocks atMu))
wconn
.
_mu
.
unlock
();
// XXX maybe `f.mu.lock() -> wconn.mu.unlock()` to avoid race with FileH close?
f
.
_mu
.
lock
();
defer
([
&
]()
{
f
.
_mu
.
unlock
();
});
for
(
auto
mmap
:
f
->
_mmaps
)
{
// TODO use ↑blk_start for binary search
if
(
!
(
mmap
->
blk_start
<=
req
->
blk
&&
req
->
blk
<
mmap
->
blk_stop
()))
...
...
@@ -302,8 +314,6 @@ error _Conn::__pin1(PinReq *req) {
}
// update f._pinned
// XXX do it before ^^^ remmapblk (so that e.g. concurrent
// discard/writeout see correct f._pinned) ?
if
(
req
->
at
==
TidHead
)
{
f
->
_pinned
.
erase
(
req
->
blk
);
// unpin to @head
}
...
...
@@ -311,7 +321,6 @@ error _Conn::__pin1(PinReq *req) {
f
->
_pinned
[
req
->
blk
]
=
req
->
at
;
}
wconn
.
_filehmu
.
unlock
();
return
nil
;
}
...
...
@@ -324,30 +333,60 @@ pair<FileH, error> _Conn::open(zodb::Oid foid) {
// XXX wconn._atMu.RLock()
// XXX defer wconn._atMu.RUnlock()
wconn
.
_filehmu
.
lock
();
defer
([
&
]()
{
wconn
.
_filehmu
.
unlock
();
});
wconn
.
_mu
.
lock
();
if
(
wconn
.
_downErr
!=
nil
)
// XXX under filehmu or downMu ?
if
(
wconn
.
_downErr
!=
nil
)
{
wconn
.
_mu
.
unlock
();
return
make_pair
(
nil
,
E
(
wconn
.
_downErr
));
}
FileH
f
;
bool
ok
;
tie
(
f
,
ok
)
=
wconn
.
_filehtab
.
get_
(
foid
);
if
(
ok
)
{
wconn
.
_mu
.
unlock
();
f
.
_openReady
.
recv
();
if
(
f
.
_openErr
!=
nil
)
return
make_pair
(
nil
,
E
(
f
.
_openErr
));
// XXX incref open count
return
make_pair
(
f
,
nil
);
}
// TODO perform open with filehmu released
// create in-flight-opening FileH entry and perform open with wconn._mu released
f
=
adoptref
(
new
_FileH
());
f
->
wconn
=
newref
(
&
wconn
);
f
->
foid
=
foid
;
f
->
_openReady
=
makechan
<
structZ
>
();
f
->
_openErr
=
nil
;
bool
retok
=
false
;
wconn
.
_filehtab
[
foid
]
=
f
;
defer
([
&
]()
{
if
(
!
retok
)
{
wconn
.
_mu
.
lock
();
wconn
.
_filehtab
.
erase
(
foid
);
wconn
.
_mu
.
unlock
();
}
f
.
_openReady
.
close
();
});
wconn
.
_mu
.
unlock
();
f
.
_openErr
=
f
.
_open
();
if
(
f
.
_openErr
!=
nil
)
return
make_pair
(
nil
,
E
(
f
.
_openErr
));
retok
=
true
;
return
make_pair
(
f
,
nil
);
}
error
_FileH
::
_open
()
{
_FileH
*
f
=
this
;
tie
(
f
->
_headf
,
err
)
=
wconn
.
_wc
->
_open
(
fmt
::
sprintf
(
"head/bigfile/%s"
,
v
(
foid
)));
if
(
err
!=
nil
)
return
make_pair
(
nil
,
E
(
err
)
);
return
make_pair
(
nil
,
err
);
bool
retok
=
false
;
defer
([
&
]()
{
...
...
@@ -358,26 +397,19 @@ pair<FileH, error> _Conn::open(zodb::Oid foid) {
struct
stat
st
;
err
=
f
->
_headf
->
stat
(
&
st
);
if
(
err
!=
nil
)
return
make_pair
(
nil
,
E
(
err
)
);
return
make_pair
(
nil
,
err
);
f
->
blksize
=
st
.
st_blksize
;
f
->
_headfsize
=
st
.
st_size
;
if
(
!
(
f
->
_headfsize
%
f
->
blksize
==
0
))
return
make_pair
(
nil
,
E
(
fmt
::
errorf
(
"wcfs bug: %s size (%d) %% blksize (%d) != 0"
,
v
(
f
->
_headf
->
name
()),
f
->
_headfsize
,
f
->
blksize
)));
wconn
.
_filehtab
[
foid
]
=
f
;
defer
([
&
]()
{
if
(
!
retok
)
wconn
.
_filehtab
.
erase
(
foid
);
});
return
make_pair
(
nil
,
fmt
::
errorf
(
"wcfs bug: %s size (%d) %% blksize (%d) != 0"
,
v
(
f
->
_headf
->
name
()),
f
->
_headfsize
,
f
->
blksize
));
// start watching f
// XXX if we start watching with holding either wconn.filehMu or f.Mu, then
// the pinner will deadlock, trying to take wconn.filehMu or f.Mu
// NOTE we are _not_ holding wconn.mu nor f.mu XXX wconn.atMu?
string
ack
;
tie
(
ack
,
err
)
=
wconn
.
_wlink
->
sendReq
(
context
::
background
(),
fmt
::
sprintf
(
"watch %s @%s"
,
v
(
foid
),
v
(
wconn
.
at
)));
if
(
err
!=
nil
)
return
make_pair
(
nil
,
E
(
err
)
);
return
make_pair
(
nil
,
err
);
if
(
ack
!=
"ok"
)
{
return
make_pair
(
nil
,
fmt
::
errorf
(
"watch: %s"
,
v
(
ack
)));
}
...
...
@@ -616,15 +648,16 @@ error _Conn::resync(zodb::Tid at) {
//
// at=TidHead means unpin to head/ .
// NOTE this does not check whether virtmem already mapped blk as RW.
//
// The following locks must be held by caller:
// - f.wconn.atMu
// - f._mu
error
_Mapping
::
_remmapblk
(
int64_t
blk
,
zodb
::
Tid
at
)
{
_Mapping
*
mmap
=
this
;
FileH
f
=
mmap
->
fileh
;
xerr
::
Contextf
E
(
"wcfs %s: conn @%s: f<%s>: remmapblk #%ld @%s"
,
v
(
f
->
wconn
->
_wc
->
mountpoint
),
v
(
f
->
wconn
->
at
),
v
(
f
->
foid
),
blk
,
v
(
at
));
// XXX locking done by callers (document)
// XXX cannot use wconn.at ^^^ because pinner does not lock wconn.atMu at all?
ASSERT
(
mmap
->
blk_start
<=
blk
&&
blk
<
mmap
->
blk_stop
());
error
err
;
...
...
@@ -677,16 +710,23 @@ error _Mapping::_remmapblk(int64_t blk, zodb::Tid at) {
// RW dirty page was e.g. discarded.
error
_Mapping
::
remmap_blk
(
int64_t
blk
)
{
_Mapping
&
mmap
=
*
this
;
FileH
f
=
mmap
->
fileh
;
// NOTE virtmem lock is held by virtmem caller
// XXX locking
// XXX locking ok?
f
.
wconn
.
_atMu
.
RLock
();
f
.
_mu
.
lock
();
defer
([
&
]()
{
f
.
_mu
.
unlock
();
f
.
wconn
.
_atMu
.
RUnlock
();
});
if
(
!
(
mmap
.
blk_start
<=
blk
&&
blk
<
mmap
.
blk_stop
()))
panic
(
"remmap_blk: blk out of Mapping range"
);
// blkrev = rev | @head
zodb
::
Tid
blkrev
;
bool
ok
;
tie
(
blkrev
,
ok
)
=
mmap
.
fileh
->
_pinned
.
get_
(
blk
);
tie
(
blkrev
,
ok
)
=
f
->
_pinned
.
get_
(
blk
);
if
(
!
ok
)
blkrev
=
TidHead
;
...
...
wcfs/client/wcfs.h
View file @
e6e6644a
...
...
@@ -174,17 +174,17 @@ struct _Conn : object {
// viewing the database at particular state. .resync write-locks this and
// knows noone is using the connection for reading simultaneously.
//
// XXX deadlock with pinner?
//
// sync::RWMutex _atMu
// XXX deadlock with pinner? -> use deadlock-avoidance similar to zwatcher in wcfs.go
sync
::
RWMutex
_atMu
zodb
::
Tid
at
;
// XXX kill downMu? (move under filehmu so that e.g. .open() can check downErr without race)
sync
::
Mutex
_downMu
;
error
_downErr
;
// !nil if connection is closed or no longer operational
//
sync::Mutex _downMu;
//
error _downErr; // !nil if connection is closed or no longer operational
sync
::
Mutex
_filehmu
;
// _atMu.W | _atMu.R + _filehMu
dict
<
zodb
::
Oid
,
FileH
>
_filehtab
;
// {} foid -> fileh
sync
::
Mutex
_mu
;
// _atMu.W | _atMu.R + _mu
error
_downErr
;
// !nil if connection is closed or no longer operational
dict
<
zodb
::
Oid
,
FileH
>
_filehTab
;
// {} foid -> fileh
sync
::
WorkGroup
_pinWG
;
// pin/unpin messages from wcfs are served by _pinner
func
<
void
()
>
_pinCancel
;
// spawned under _pinWG.
...
...
@@ -225,8 +225,11 @@ struct _FileH : object {
size_t
blksize
;
// block size of this file (does not change after fileh open)
os
::
File
_headf
;
// file object of head/file
off_t
_headfsize
;
// head/file size is known to be at least headfsize (size ↑=)
// head/file size is known to be at least headfsize (size ↑=)
// protected by .wconn._atMu
off_t
_headfsize
;
sync
::
Mutex
_mu
;
// atMu.W | atMu.R + _mu
dict
<
int64_t
,
zodb
::
Tid
>
_pinned
;
// {} blk -> rev that wcfs already sent us for this file
vector
<
Mapping
>
_mmaps
;
// []Mapping ↑blk_start mappings of this file
...
...
@@ -248,7 +251,7 @@ public:
// The mapped memory is [.mem_start, .mem_stop)
// Use .unmap to release virtual memory resources used by mapping.
//
//
I
t is safe to use Mapping from multiple threads simultaneously.
//
Except unmap, i
t is safe to use Mapping from multiple threads simultaneously.
typedef
refptr
<
struct
_Mapping
>
Mapping
;
struct
_Mapping
:
object
{
FileH
fileh
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment