2
2
import contextlib
3
3
import json
4
4
import os
5
+ from random import random
5
6
import socket
7
+ import time
6
8
7
9
import pytest
8
10
import zc .lockfile
@@ -80,23 +82,39 @@ def unlock_display(display, lock_dir, services_log):
80
82
return unlock_resource ('display' , display , lock_dir , services_log )
81
83
82
84
83
- def lock_resource (name , resource_getter , lock_dir , services_log ):
84
- """Issue a lock for given resource."""
85
- with locked_resources (name , lock_dir ) as bound_resources :
86
- services_log .debug ('bound_resources {0}: {1}' .format (name , bound_resources ))
87
-
88
- resource = resource_getter (bound_resources )
89
- while resource in bound_resources :
90
- # resource is already taken by someone, retry
91
- services_log .debug ('bound resources {0}: {1}' .format (name , bound_resources ))
92
- resource = resource_getter (bound_resources )
93
- services_log .debug ('free resource choosen {0}: {1}' .format (name , resource ))
94
- bound_resources .append (resource )
95
- services_log .debug ('bound resources {0}: {1}' .format (name , bound_resources ))
96
- return resource
85
+ @pytest .fixture (scope = 'session' )
86
+ def lock_resource_timeout ():
87
+ """Max number of seconds to obtain the lock."""
88
+ return 20
97
89
98
90
99
- def get_free_port (lock_dir , services_log ):
91
+ def lock_resource (name , resource_getter , lock_dir , services_log , lock_resource_timeout ):
92
+ """Issue a lock for given resource."""
93
+ total_seconds_slept = 0
94
+ while True :
95
+ try :
96
+ with locked_resources (name , lock_dir ) as bound_resources :
97
+ services_log .debug ('bound_resources {0}: {1}' .format (name , bound_resources ))
98
+ resource = resource_getter (bound_resources )
99
+ while resource in bound_resources :
100
+ # resource is already taken by someone, retry
101
+ services_log .debug ('bound resources {0}: {1}' .format (name , bound_resources ))
102
+ resource = resource_getter (bound_resources )
103
+ services_log .debug ('free resource choosen {0}: {1}' .format (name , resource ))
104
+ bound_resources .append (resource )
105
+ services_log .debug ('bound resources {0}: {1}' .format (name , bound_resources ))
106
+ return resource
107
+ except zc .lockfile .LockError as err :
108
+ if total_seconds_slept >= lock_resource_timeout :
109
+ raise err
110
+ services_log .debug ('lock resource failed: {0}' .format (err ))
111
+
112
+ seconds_to_sleep = random () * 0.1 + 0.05
113
+ total_seconds_slept += seconds_to_sleep
114
+ time .sleep (seconds_to_sleep )
115
+
116
+
117
+ def get_free_port (lock_dir , services_log , lock_resource_timeout ):
100
118
"""Get free port to listen on."""
101
119
def get_port (bound_resources ):
102
120
if bound_resources :
@@ -114,10 +132,10 @@ def get_port(bound_resources):
114
132
pass
115
133
port += 1
116
134
117
- return lock_resource ('port' , get_port , lock_dir , services_log )
135
+ return lock_resource ('port' , get_port , lock_dir , services_log , lock_resource_timeout )
118
136
119
137
120
- def get_free_display (lock_dir , services_log ):
138
+ def get_free_display (lock_dir , services_log , lock_resource_timeout ):
121
139
"""Get free display to listen on."""
122
140
def get_display (bound_resources ):
123
141
display = 100
@@ -129,15 +147,15 @@ def get_display(bound_resources):
129
147
continue
130
148
return display
131
149
132
- return lock_resource ('display' , get_display , lock_dir , services_log )
150
+ return lock_resource ('display' , get_display , lock_dir , services_log , lock_resource_timeout )
133
151
134
152
135
153
@pytest .fixture (scope = 'session' )
136
- def port_getter (request , lock_dir , services_log ):
154
+ def port_getter (request , lock_dir , services_log , lock_resource_timeout ):
137
155
"""Lock getter function."""
138
156
def get_port ():
139
157
"""Lock a free port and unlock it on finalizer."""
140
- port = get_free_port (lock_dir , services_log )
158
+ port = get_free_port (lock_dir , services_log , lock_resource_timeout )
141
159
142
160
def finalize ():
143
161
unlock_port (port , lock_dir , services_log )
@@ -147,11 +165,11 @@ def finalize():
147
165
148
166
149
167
@pytest .fixture (scope = 'session' )
150
- def display_getter (request , lock_dir , services_log ):
168
+ def display_getter (request , lock_dir , services_log , lock_resource_timeout ):
151
169
"""Display getter function."""
152
170
def get_display ():
153
171
"""Lock a free display and unlock it on finalizer."""
154
- display = get_free_display (lock_dir , services_log )
172
+ display = get_free_display (lock_dir , services_log , lock_resource_timeout )
155
173
request .addfinalizer (lambda : unlock_display (display , lock_dir , services_log ))
156
174
return display
157
175
return get_display
0 commit comments