/
container.py
225 lines (181 loc) · 8.04 KB
/
container.py
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Hive Netius System
# Copyright (c) 2008-2019 Hive Solutions Lda.
#
# This file is part of Hive Netius System.
#
# Hive Netius System is free software: you can redistribute it and/or modify
# it under the terms of the Apache License as published by the Apache
# Foundation, either version 2.0 of the License, or (at your option) any
# later version.
#
# Hive Netius System is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Apache License for more details.
#
# You should have received a copy of the Apache License along with
# Hive Netius System. If not, see <http://www.apache.org/licenses/>.
__author__ = "João Magalhães <joamag@hive.pt>"
""" The author(s) of the module """
__version__ = "1.0.0"
""" The version of the module """
__revision__ = "$LastChangedRevision$"
""" The revision number of the module """
__date__ = "$LastChangedDate$"
""" The last change date of the module """
__copyright__ = "Copyright (c) 2008-2019 Hive Solutions Lda."
""" The copyright for the module """
__license__ = "Apache License, Version 2.0"
""" The license for the module """
from . import server
from .common import * #@UnusedWildImport pylint: disable=W0614
class Container(Base):
def __init__(self, *args, **kwargs):
Base.__init__(self, *args, **kwargs)
self.owner = None
self.bases = []
def start(self, owner):
# sets the current polling structure of the owner in the container
# it's important to use the already initialized poll in the container
# so that the requested environment (host, port , etc.) is used note
# that event the same logger is used for the container (logic propagation)
self.owner = owner
self.poll_c = owner.poll_c
self.poll = owner.poll
self.poll_name = owner.poll_name
self.poll_timeout = owner.poll_timeout
self.level = owner.level
self.logger = owner.logger
self._loaded = True
# sets the owner of the current container as the owner of the poll
# this avoids problems with the cleanup operation for the poll
owner.poll_owner = True
# runs the starting operation in the complete set of base structures
# registered under the container, this is the required operation in
# order to propagate the changed in the container to the bases
self.start_all()
# calls the super method of the base for the current container this should
# start the event loop for the container (blocking call)
Base.start(self)
def cleanup(self):
Base.cleanup(self)
# unsets the owner of the container, this should diminish the chance of
# memory leaks due to cycle references (required to avoid problems)
self.owner = None
# iterates over all the bases registered and propagates the cleanup operation
# over them, deleting the list of bases afterwards (no more usage for them)
for base in self.bases: base.cleanup()
del self.bases[:]
# unbinds the start operation from the on start event, as this is no longer
# required, should re-register for it on "next start" event
self.unbind("start", self.on_start)
def loop(self):
# iterates continuously while the running flag
# is set, once it becomes unset the loop breaks
# at the next execution cycle
while self._running:
# calls the base tick int handler indicating that a new
# tick loop iteration is going to be started, all the
# "in between loop" operation should be performed in this
# callback as this is the "space" they have for execution
self.ticks()
# updates the current state to poll to indicate
# that the base service is selecting the connections
self.set_state(STATE_POLL)
# runs the "owner" based version of the poll operation
# so that the poll results are indexed by their owner
# reference to be easily routed to the base services
result = self.poll.poll_owner()
for base, values in legacy.iteritems(result):
reads, writes, errors = values
base.reads(reads)
base.writes(writes)
base.errors(errors)
def ticks(self):
self.set_state(STATE_TICK)
self._lid = (self._lid + 1) % 2147483647
for base in self.bases: base.ticks()
def connections_dict(self, full = False):
all = dict()
for base in self.bases:
is_owner = base == self.owner
if is_owner: connections = base.connections_dict(
full = full, parent = True
)
else: connections = base.connections_dict(full = full)
all[base.name] = connections
return all
def connection_dict(self, id, full = False):
connection = None
for base in self.bases:
for _connection in base.connections:
if not _connection.id == id: continue
connection = _connection
break
if connection: break
if not connection: return None
return connection.info_dict(full = full)
def on_start(self):
Base.on_start(self)
self.apply_all()
self.call_all("on_start")
def on_stop(self):
Base.on_stop(self)
self.call_all("on_stop")
def add_base(self, base):
self.apply_base(base)
self.bases.append(base)
def remove_base(self, base):
self.bases.remove(base)
def start_base(self, base):
base.level = self.level
base.logger = self.logger
base.load()
def start_all(self):
for base in self.bases: self.start_base(base)
def apply_all(self):
for base in self.bases: self.apply_base(base)
def apply_base(self, base):
base.tid = self.tid
base.poll = self.poll
base.level = self.level
base.logger = self.logger
base.poll_owner = base == self.owner
def call_all(self, name, *args, **kwargs):
for base in self.bases:
method = getattr(base, name)
method(*args, **kwargs)
def trigger_all(self, name, *args, **kwargs):
for base in self.bases: base.trigger(name, base, *args, **kwargs)
class ContainerServer(server.StreamServer):
def __init__(self, *args, **kwargs):
server.StreamServer.__init__(self, *args, **kwargs)
self.container = Container(*args, **kwargs)
self.add_base(self)
def start(self):
# starts the container this should trigger the start of the
# event loop in the container and the proper listening of all
# the connections in the current environment
self.container.start(self)
def stop(self):
# verifies if there's a container object currently defined in
# the object and in case it does exist propagates the stop call
# to the container so that the proper stop operation is performed
if not self.container: return
self.container.stop()
def cleanup(self):
server.StreamServer.cleanup(self)
# saves the container reference so that it may be used latter
# and then unsets it under the current instance
container = self.container
self.container = None
# verifies if the container is valid and if that's not the case
# returns the control flow immediately (as expected)
if not container: return
# runs the cleanup operation on the cleanup, this should properly
# propagate the operation to the owner container (as expected)
container.cleanup()
def add_base(self, base):
self.container.add_base(base)