1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
// $Id$
#ifndef CLIENT_ACCEPTOR_H
#define CLIENT_ACCEPTOR_H
/*
The ACE_Acceptor<> template lives in the ace/Acceptor.h header file. You'll
find a very consitent naming convention between the ACE objects and the
headers where they can be found. In general, the ACE object ACE_Foobar will
be found in ace/Foobar.h.
*/
#include "ace/Acceptor.h"
#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
/*
Since we want to work with sockets, we'll need a SOCK_Acceptor to allow the
clients to connect to us.
*/
#include "ace/SOCK_Acceptor.h"
/*
The Client_Handler object we develop will be used to handle clients once
they're connected. The ACE_Acceptor<> template's first parameter requires
such an object. In some cases, you can get by with just a forward
declaration on the class, in others you have to have the whole thing.
*/
#include "client_handler.h"
/*
Parameterize the ACE_Acceptor<> such that it will listen for socket
connection attempts and create Client_Handler objects when they happen. In
Tutorial 001, we wrote the basic acceptor logic on our own before we
realized that ACE_Acceptor<> was available. You'll get spoiled using the
ACE templates because they take away a lot of the tedious details!
*/
typedef ACE_Acceptor < Client_Handler, ACE_SOCK_ACCEPTOR > Client_Acceptor_Base;
#include "thread_pool.h"
/*
This time we've added quite a bit more to our acceptor. In addition to
providing a choice of concurrency strategies, we also maintain a Thread_Pool
object in case that strategy is chosen. The object still isn't very complex
but it's come a long way from the simple typedef we had in Tutorial 5.
Why keep the thread pool as a member? If we go back to the inetd concept
you'll recall that we need several acceptors to make that work. We may have
a situation in which our different client types requre different resources.
That is, we may need a large thread pool for some client types and a smaller
one for others. We could share a pool but then the client types may have
undesirable impact on one another.
Just in case you do want to share a single thread pool, there is a constructor
below that will let you do that.
*/
class Client_Acceptor : public Client_Acceptor_Base
{
public:
typedef Client_Acceptor_Base inherited;
/*
Now that we have more than two strategies, we need more than a boolean
to tell us what we're using. A set of enums is a good choice because
it allows us to use named values. Another option would be a set of
static const integers.
*/
enum concurrency_t
{
single_threaded_,
thread_per_connection_,
thread_pool_
};
/*
The default constructor allows the programmer to choose the concurrency
strategy. Since we want to focus on thread-pool, that's what we'll use
if nothing is specified.
*/
Client_Acceptor( int _concurrency = thread_pool_ );
/*
Another option is to construct the object with an existing thread pool.
The concurrency strategy is pretty obvious at that point.
*/
Client_Acceptor( Thread_Pool & _thread_pool );
/*
Our destructor will take care of shutting down the thread-pool
if applicable.
*/
~Client_Acceptor( void );
/*
Open ourselves and register with the given reactor. The thread pool size
can be specified here if you want to use that concurrency strategy.
*/
int open( const ACE_INET_Addr & _addr, ACE_Reactor * _reactor,
int _pool_size = Thread_Pool::default_pool_size_ );
/*
Close ourselves and our thread pool if applicable
*/
int close(void);
/*
What is our concurrency strategy?
*/
int concurrency(void)
{ return this->concurrency_; }
/*
Give back a pointer to our thread pool. Our Client_Handler objects
will need this so that their handle_input() methods can put themselves
into the pool. Another alternative would be a globally accessible
thread pool. ACE_Singleton<> is a way to achieve that.
*/
Thread_Pool * thread_pool(void)
{ return & this->the_thread_pool_; }
/*
Since we can be constructed with a Thread_Pool reference, there are times
when we need to know if the thread pool we're using is ours or if we're
just borrowing it from somebody else.
*/
int thread_pool_is_private(void)
{ return &the_thread_pool_ == &private_thread_pool_; }
protected:
int concurrency_;
Thread_Pool private_thread_pool_;
Thread_Pool & the_thread_pool_;
};
#endif // CLIENT_ACCEPTOR_H
|