Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
497ad343
Commit
497ad343
authored
Aug 27, 2015
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use stateful brokers in CURL example
parent
a77b9138
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
191 additions
and
236 deletions
+191
-236
examples/curl/curl_fuse.cpp
examples/curl/curl_fuse.cpp
+191
-236
No files found.
examples/curl/curl_fuse.cpp
View file @
497ad343
...
@@ -97,180 +97,171 @@ constexpr int min_req_interval = 10;
...
@@ -97,180 +97,171 @@ constexpr int min_req_interval = 10;
// maximum delay between HTTP requests
// maximum delay between HTTP requests
constexpr
int
max_req_interval
=
300
;
constexpr
int
max_req_interval
=
300
;
// provides print utility and each base_actor has a parent
// put everything into anonymous namespace (except main)
class
base_actor
:
public
event_based_actor
{
namespace
{
protected:
base_actor
(
actor
parent
,
std
::
string
name
,
std
::
string
color_code
)
// provides print utility, a name, and a parent
:
parent_
(
std
::
move
(
parent
)),
struct
base_state
{
name_
(
std
::
move
(
name
)),
base_state
(
local_actor
*
thisptr
)
:
self
(
thisptr
)
{
color_
(
std
::
move
(
color_code
)),
out_
(
this
)
{
// nop
// nop
}
}
~
base_actor
();
actor_ostream
print
()
{
return
aout
(
self
)
<<
color
<<
name
<<
" (id = "
<<
self
->
id
()
<<
"): "
;
inline
actor_ostream
&
print
()
{
return
out_
<<
color_
<<
name_
<<
" (id = "
<<
id
()
<<
"): "
;
}
}
void
on_exit
()
{
virtual
void
init
(
actor
m_parent
,
std
::
string
m_name
,
std
::
string
m_color
)
{
print
()
<<
"on_exit"
<<
color
::
reset_endl
;
parent
=
std
::
move
(
m_parent
);
name
=
std
::
move
(
m_name
);
color
=
std
::
move
(
m_color
);
print
()
<<
"started"
<<
color
::
reset_endl
;
}
}
actor
parent_
;
virtual
~
base_state
()
{
print
()
<<
"done"
<<
color
::
reset_endl
;
}
private:
local_actor
*
self
;
std
::
string
name_
;
actor
parent
;
std
::
string
color_
;
std
::
string
name
;
actor_ostream
out_
;
std
::
string
color
;
};
};
base_actor
::~
base_actor
()
{
// avoid weak-vtables warning
}
// encapsulates an HTTP request
// encapsulates an HTTP request
class
client_job
:
public
base_actor
{
behavior
client_job
(
stateful_actor
<
base_state
>*
self
,
actor
parent
)
{
public:
self
->
state
.
init
(
std
::
move
(
parent
),
"client-job"
,
color
::
blue
);
explicit
client_job
(
actor
parent
)
self
->
send
(
self
->
state
.
parent
,
read_atom
::
value
,
:
base_actor
(
std
::
move
(
parent
),
"client_job"
,
color
::
blue
)
{
"http://www.example.com/index.html"
,
// nop
}
~
client_job
();
protected:
behavior
make_behavior
()
override
{
print
()
<<
"init"
<<
color
::
reset_endl
;
send
(
parent_
,
read_atom
::
value
,
"http://www.example.com/index.html"
,
uint64_t
{
0
},
uint64_t
{
4095
});
uint64_t
{
0
},
uint64_t
{
4095
});
return
{
return
{
[
=
](
reply_atom
,
const
buffer_type
&
buf
)
{
[
=
](
reply_atom
,
const
buffer_type
&
buf
)
{
print
()
<<
"successfully received "
<<
buf
.
size
()
<<
" bytes"
self
->
state
.
print
()
<<
"successfully received "
<<
buf
.
size
()
<<
" bytes"
<<
color
::
reset_endl
;
<<
color
::
reset_endl
;
quit
();
self
->
quit
();
},
},
[
=
](
fail_atom
)
{
[
=
](
fail_atom
)
{
print
()
<<
"failure"
<<
color
::
reset_endl
;
self
->
state
.
print
()
<<
"failure"
<<
color
::
reset_endl
;
quit
();
self
->
quit
();
}
}
};
};
}
};
client_job
::~
client_job
()
{
// avoid weak-vtables warning
}
}
// spawns HTTP requests
struct
client_state
:
base_state
{
class
client
:
public
base_actor
{
client_state
(
local_actor
*
self
)
public:
:
base_state
(
self
),
count
(
0
),
explicit
client
(
const
actor
&
parent
)
re
(
rd
()),
:
base_actor
(
parent
,
"client"
,
color
::
green
),
dist
(
min_req_interval
,
max_req_interval
)
{
count_
(
0
),
re_
(
rd_
()),
dist_
(
min_req_interval
,
max_req_interval
)
{
// nop
// nop
}
}
~
client
();
size_t
count
;
std
::
random_device
rd
;
std
::
default_random_engine
re
;
std
::
uniform_int_distribution
<
int
>
dist
;
};
protected:
// spawns HTTP requests
behavior
make_behavior
()
override
{
behavior
client
(
stateful_actor
<
client_state
>*
self
,
actor
parent
)
{
using
std
::
chrono
::
milliseconds
;
using
std
::
chrono
::
milliseconds
;
link_to
(
parent_
);
self
->
link_to
(
parent
);
print
()
<<
"init"
<<
color
::
reset_endl
;
self
->
state
.
init
(
std
::
move
(
parent
),
"client"
,
color
::
green
);
// start 'loop'
self
->
send
(
self
,
next_atom
::
value
);
send
(
this
,
next_atom
::
value
);
return
{
return
(
[
=
](
next_atom
)
{
[
=
](
next_atom
)
{
print
()
<<
"spawn new client_job (nr. "
auto
&
st
=
self
->
state
;
<<
++
count_
st
.
print
()
<<
"spawn new client_job (nr. "
<<
++
st
.
count
<<
")"
<<
")"
<<
color
::
reset_endl
;
<<
color
::
reset_endl
;
// client_job will use IO
// client_job will use IO
// and should thus be spawned in a separate thread
// and should thus be spawned in a separate thread
spawn
<
client_job
,
detached
+
linked
>
(
parent_
);
self
->
spawn
<
detached
+
linked
>
(
client_job
,
st
.
parent
);
// compute random delay until next job is launched
// compute random delay until next job is launched
auto
delay
=
dist_
(
re_
);
auto
delay
=
st
.
dist
(
st
.
re
);
delayed_send
(
this
,
milliseconds
(
delay
),
next_atom
::
value
);
self
->
delayed_send
(
self
,
milliseconds
(
delay
),
next_atom
::
value
);
}
}
);
};
}
struct
curl_state
:
base_state
{
curl_state
(
local_actor
*
self
)
:
base_state
(
self
)
{
// nop
}
}
private:
~
curl_state
()
{
size_t
count_
;
if
(
curl
)
std
::
random_device
rd_
;
curl_easy_cleanup
(
curl
);
std
::
default_random_engine
re_
;
}
std
::
uniform_int_distribution
<
int
>
dist_
;
};
client
::~
client
()
{
static
size_t
callback
(
void
*
data
,
size_t
bsize
,
size_t
nmemb
,
void
*
userp
)
{
// avoid weak-vtables warning
size_t
size
=
bsize
*
nmemb
;
}
auto
&
buf
=
reinterpret_cast
<
curl_state
*>
(
userp
)
->
buf
;
auto
first
=
reinterpret_cast
<
char
*>
(
data
);
auto
last
=
first
+
bsize
;
buf
.
insert
(
buf
.
end
(),
first
,
last
);
return
size
;
}
// manages a CURL session
void
init
(
actor
m_parent
,
std
::
string
m_name
,
std
::
string
m_color
)
override
{
class
curl_worker
:
public
base_actor
{
curl
=
curl_easy_init
();
public:
if
(
!
curl
)
explicit
curl_worker
(
const
actor
&
parent
)
throw
std
::
runtime_error
(
"Unable initialize CURL."
);
:
base_actor
(
parent
,
"curl_worker"
,
color
::
yellow
),
curl_easy_setopt
(
curl
,
CURLOPT_WRITEFUNCTION
,
&
curl_state
::
callback
);
curl_
(
nullptr
)
{
curl_easy_setopt
(
curl
,
CURLOPT_NOSIGNAL
,
1
);
// nop
base_state
::
init
(
std
::
move
(
m_parent
),
std
::
move
(
m_name
),
std
::
move
(
m_color
));
}
}
~
curl_worker
();
CURL
*
curl
=
nullptr
;
buffer_type
buf
;
};
protected:
// manages a CURL session
behavior
make_behavior
()
override
{
behavior
curl_worker
(
stateful_actor
<
curl_state
>*
self
,
actor
parent
)
{
print
()
<<
"init"
<<
color
::
reset_endl
;
self
->
state
.
init
(
std
::
move
(
parent
),
"curl-worker"
,
color
::
yellow
);
curl_
=
curl_easy_init
();
return
{
curl_easy_setopt
(
curl_
,
CURLOPT_WRITEFUNCTION
,
&
curl_worker
::
cb
);
curl_easy_setopt
(
curl_
,
CURLOPT_NOSIGNAL
,
1
);
return
(
[
=
](
read_atom
,
const
std
::
string
&
fname
,
uint64_t
offset
,
uint64_t
range
)
[
=
](
read_atom
,
const
std
::
string
&
fname
,
uint64_t
offset
,
uint64_t
range
)
->
message
{
->
message
{
print
()
<<
"read"
<<
color
::
reset_endl
;
auto
&
st
=
self
->
state
;
st
.
print
()
<<
"read"
<<
color
::
reset_endl
;
for
(;;)
{
for
(;;)
{
buf_
.
clear
();
st
.
buf
.
clear
();
// set URL
// set URL
curl_easy_setopt
(
curl_
,
CURLOPT_URL
,
fname
.
c_str
());
curl_easy_setopt
(
st
.
curl
,
CURLOPT_URL
,
fname
.
c_str
());
// set range
// set range
std
::
ostringstream
oss
;
std
::
ostringstream
oss
;
oss
<<
offset
<<
"-"
<<
range
;
oss
<<
offset
<<
"-"
<<
range
;
curl_easy_setopt
(
curl_
,
CURLOPT_RANGE
,
oss
.
str
().
c_str
());
curl_easy_setopt
(
st
.
curl
,
CURLOPT_RANGE
,
oss
.
str
().
c_str
());
// set curl callback
// set curl callback
curl_easy_setopt
(
curl_
,
CURLOPT_WRITEDATA
,
curl_easy_setopt
(
st
.
curl
,
CURLOPT_WRITEDATA
,
reinterpret_cast
<
void
*>
(
this
));
reinterpret_cast
<
void
*>
(
&
st
));
// launch file transfer
// launch file transfer
auto
res
=
curl_easy_perform
(
curl_
);
auto
res
=
curl_easy_perform
(
st
.
curl
);
if
(
res
!=
CURLE_OK
)
{
if
(
res
!=
CURLE_OK
)
{
print
()
<<
"curl_easy_perform() failed: "
st
.
print
()
<<
"curl_easy_perform() failed: "
<<
curl_easy_strerror
(
res
)
<<
curl_easy_strerror
(
res
)
<<
color
::
reset_endl
;
<<
color
::
reset_endl
;
}
else
{
}
else
{
long
hc
=
0
;
// http return code
long
hc
=
0
;
// http return code
curl_easy_getinfo
(
curl_
,
CURLINFO_RESPONSE_CODE
,
&
hc
);
curl_easy_getinfo
(
st
.
curl
,
CURLINFO_RESPONSE_CODE
,
&
hc
);
switch
(
hc
)
{
switch
(
hc
)
{
default:
default:
print
()
<<
"http error: download failed with "
st
.
print
()
<<
"http error: download failed with "
<<
"'HTTP RETURN CODE': "
<<
"'HTTP RETURN CODE': "
<<
hc
<<
hc
<<
color
::
reset_endl
;
<<
color
::
reset_endl
;
break
;
break
;
case
200
:
// ok
case
200
:
// ok
case
206
:
// partial content
case
206
:
// partial content
print
()
<<
"received "
st
.
print
()
<<
"received "
<<
buf_
.
size
()
<<
st
.
buf
.
size
()
<<
" bytes with 'HTTP RETURN CODE': "
<<
" bytes with 'HTTP RETURN CODE': "
<<
hc
<<
hc
<<
color
::
reset_endl
;
<<
color
::
reset_endl
;
// tell parent that this worker is done
// tell parent that this worker is done
send
(
parent_
,
finished_atom
::
value
);
self
->
send
(
st
.
parent
,
finished_atom
::
value
);
return
make_message
(
reply_atom
::
value
,
buf_
);
return
make_message
(
reply_atom
::
value
,
std
::
move
(
st
.
buf
)
);
case
404
:
// file does not exist
case
404
:
// file does not exist
print
()
<<
"http error: download failed with "
st
.
print
()
<<
"http error: download failed with "
<<
"'HTTP RETURN CODE': 404 (file does "
<<
"'HTTP RETURN CODE': 404 (file does "
<<
"not exist!)"
<<
"not exist!)"
<<
color
::
reset_endl
;
<<
color
::
reset_endl
;
...
@@ -280,77 +271,51 @@ protected:
...
@@ -280,77 +271,51 @@ protected:
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
milliseconds
(
100
));
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
milliseconds
(
100
));
}
}
}
}
);
};
}
void
on_exit
()
{
curl_easy_cleanup
(
curl_
);
print
()
<<
"on_exit"
<<
color
::
reset_endl
;
}
private:
static
size_t
cb
(
void
*
data
,
size_t
bsize
,
size_t
nmemb
,
void
*
userp
)
{
size_t
size
=
bsize
*
nmemb
;
auto
&
buf
=
reinterpret_cast
<
curl_worker
*>
(
userp
)
->
buf_
;
auto
first
=
reinterpret_cast
<
char
*>
(
data
);
auto
last
=
first
+
bsize
;
buf
.
insert
(
buf
.
end
(),
first
,
last
);
return
size
;
}
CURL
*
curl_
;
buffer_type
buf_
;
};
curl_worker
::~
curl_worker
()
{
// avoid weak-vtables warning
}
}
// manages {num_curl_workers} workers with a round-robin protocol
struct
master_state
:
base_state
{
class
curl_master
:
public
base_actor
{
master_state
(
local_actor
*
self
)
:
base_state
(
self
)
{
public:
curl_master
()
:
base_actor
(
invalid_actor
,
"curl_master"
,
color
::
magenta
)
{
// nop
// nop
}
}
std
::
vector
<
actor
>
idle
;
std
::
vector
<
actor
>
busy
;
};
~
curl_master
();
behavior
curl_master
(
stateful_actor
<
master_state
>*
self
)
{
self
->
state
.
init
(
invalid_actor
,
"curl-master"
,
color
::
magenta
);
protected:
behavior
make_behavior
()
override
{
print
()
<<
"init"
<<
color
::
reset_endl
;
// spawn workers
// spawn workers
for
(
size_t
i
=
0
;
i
<
num_curl_workers
;
++
i
)
{
for
(
size_t
i
=
0
;
i
<
num_curl_workers
;
++
i
)
idle_worker_
.
push_back
(
spawn
<
curl_worker
,
detached
+
linked
>
(
this
));
self
->
state
.
idle
.
push_back
(
self
->
spawn
<
detached
+
linked
>
(
curl_worker
,
self
));
}
auto
worker_finished
=
[
=
]
{
auto
worker_finished
=
[
=
]
{
auto
sender
=
current_sender
();
auto
sender
=
self
->
current_sender
();
auto
last
=
busy_worker_
.
end
();
auto
last
=
self
->
state
.
busy
.
end
();
auto
i
=
std
::
find
(
busy_worker_
.
begin
(),
last
,
sender
);
auto
i
=
std
::
find
(
self
->
state
.
busy
.
begin
(),
last
,
sender
);
if
(
i
==
last
)
{
if
(
i
==
last
)
return
;
return
;
}
self
->
state
.
idle
.
push_back
(
*
i
);
idle_worker_
.
push_back
(
*
i
);
self
->
state
.
busy
.
erase
(
i
);
busy_worker_
.
erase
(
i
);
self
->
state
.
print
()
<<
"worker is done"
<<
color
::
reset_endl
;
print
()
<<
"worker is done"
<<
color
::
reset_endl
;
};
};
print
()
<<
"spawned "
<<
idle_worker_
.
size
()
self
->
state
.
print
()
<<
"spawned "
<<
self
->
state
.
idle
.
size
()
<<
" worker(s)"
<<
color
::
reset_endl
;
<<
" worker(s)"
<<
color
::
reset_endl
;
return
{
return
{
[
=
](
read_atom
,
const
std
::
string
&
,
uint64_t
,
uint64_t
)
{
[
=
](
read_atom
,
const
std
::
string
&
,
uint64_t
,
uint64_t
)
{
print
()
<<
"received {'read'}"
<<
color
::
reset_endl
;
auto
&
st
=
self
->
state
;
st
.
print
()
<<
"received {'read'}"
<<
color
::
reset_endl
;
// forward job to an idle worker
// forward job to an idle worker
actor
worker
=
idle_worker_
.
back
();
actor
worker
=
st
.
idle
.
back
();
idle_worker_
.
pop_back
();
st
.
idle
.
pop_back
();
busy_worker_
.
push_back
(
worker
);
st
.
busy
.
push_back
(
worker
);
forward_to
(
worker
);
self
->
forward_to
(
worker
);
print
()
<<
busy_worker_
.
size
()
<<
" active jobs"
<<
color
::
reset_endl
;
st
.
print
()
<<
st
.
busy
.
size
()
<<
" active jobs"
<<
color
::
reset_endl
;
if
(
idle_worker_
.
empty
())
{
if
(
st
.
idle
.
empty
())
{
// wait until at least one worker finished its job
// wait until at least one worker finished its job
become
(
self
->
become
(
keep_behavior
,
keep_behavior
,
[
=
](
finished_atom
)
{
[
=
](
finished_atom
)
{
worker_finished
();
worker_finished
();
unbecome
();
self
->
unbecome
();
}
}
);
);
}
}
...
@@ -359,20 +324,11 @@ protected:
...
@@ -359,20 +324,11 @@ protected:
worker_finished
();
worker_finished
();
}
}
};
};
}
private:
std
::
vector
<
actor
>
idle_worker_
;
std
::
vector
<
actor
>
busy_worker_
;
};
curl_master
::~
curl_master
()
{
// avoid weak-vtables warning
}
}
// signal handling for ctrl+c
// signal handling for ctrl+c
namespace
{
std
::
atomic
<
bool
>
shutdown_flag
{
false
};
std
::
atomic
<
bool
>
shutdown_flag
{
false
};
}
// namespace <anonymous>
}
// namespace <anonymous>
int
main
()
{
int
main
()
{
...
@@ -392,12 +348,11 @@ int main() {
...
@@ -392,12 +348,11 @@ int main() {
{
// lifetime scope of self
{
// lifetime scope of self
scoped_actor
self
;
scoped_actor
self
;
// spawn client and curl_master
// spawn client and curl_master
auto
master
=
self
->
spawn
<
curl_master
,
detached
>
(
);
auto
master
=
self
->
spawn
<
detached
>
(
curl_master
);
self
->
spawn
<
client
,
detached
>
(
master
);
self
->
spawn
<
detached
>
(
client
,
master
);
// poll CTRL+C flag every second
// poll CTRL+C flag every second
while
(
!
shutdown_flag
)
{
while
(
!
shutdown_flag
)
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
seconds
(
1
));
std
::
this_thread
::
sleep_for
(
std
::
chrono
::
seconds
(
1
));
}
aout
(
self
)
<<
color
::
cyan
<<
"received CTRL+C"
<<
color
::
reset_endl
;
aout
(
self
)
<<
color
::
cyan
<<
"received CTRL+C"
<<
color
::
reset_endl
;
// shutdown actors
// shutdown actors
anon_send_exit
(
master
,
exit_reason
::
user_shutdown
);
anon_send_exit
(
master
,
exit_reason
::
user_shutdown
);
...
...
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