4949
5050from six import python_2_unicode_compatible
5151from . import numbertheory
52- from ._rwlock import RWLock
5352
5453
5554@python_2_unicode_compatible
@@ -167,88 +166,64 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
167166 cause to precompute multiplication table generation for it
168167 """
169168 self .__curve = curve
170- # since it's generally better (faster) to use scaled points vs unscaled
171- # ones, use writer-biased RWLock for locking:
172- self ._update_lock = RWLock ()
173169 if GMPY : # pragma: no branch
174- self .__x = mpz (x )
175- self .__y = mpz (y )
176- self .__z = mpz (z )
170+ self .__coords = (mpz (x ), mpz (y ), mpz (z ))
177171 self .__order = order and mpz (order )
178172 else : # pragma: no branch
179- self .__x = x
180- self .__y = y
181- self .__z = z
173+ self .__coords = (x , y , z )
182174 self .__order = order
183175 self .__generator = generator
184176 self .__precompute = []
185177
186178 def _maybe_precompute (self ):
187- if self .__generator :
188- # since we lack promotion of read-locks to write-locks, we do a
189- # "acquire-read-lock, check, acquire-write-lock plus recheck" cycle
190- try :
191- self ._update_lock .reader_acquire ()
192- if self .__precompute :
193- return
194- finally :
195- self ._update_lock .reader_release ()
196-
197- try :
198- self ._update_lock .writer_acquire ()
199- if self .__precompute :
200- return
201- order = self .__order
202- assert order
203- i = 1
204- order *= 2
205- doubler = PointJacobi (
206- self .__curve , self .__x , self .__y , self .__z , order
207- )
208- order *= 2
209- self .__precompute .append ((doubler .x (), doubler .y ()))
210-
211- while i < order :
212- i *= 2
213- doubler = doubler .double ().scale ()
214- self .__precompute .append ((doubler .x (), doubler .y ()))
215-
216- finally :
217- self ._update_lock .writer_release ()
179+ if not self .__generator or self .__precompute :
180+ return
181+
182+ # since this code will execute just once, and it's fully deterministic,
183+ # depend on atomicity of the last assignment to switch from empty
184+ # self.__precompute to filled one and just ignore the unlikely
185+ # situation when two threads execute it at the same time (as it won't
186+ # lead to inconsistent __precompute)
187+ order = self .__order
188+ assert order
189+ precompute = []
190+ i = 1
191+ order *= 2
192+ coord_x , coord_y , coord_z = self .__coords
193+ doubler = PointJacobi (self .__curve , coord_x , coord_y , coord_z , order )
194+ order *= 2
195+ precompute .append ((doubler .x (), doubler .y ()))
196+
197+ while i < order :
198+ i *= 2
199+ doubler = doubler .double ().scale ()
200+ precompute .append ((doubler .x (), doubler .y ()))
201+
202+ self .__precompute = precompute
218203
219204 def __getstate__ (self ):
220- try :
221- self ._update_lock .reader_acquire ()
222- state = self .__dict__ .copy ()
223- finally :
224- self ._update_lock .reader_release ()
225- del state ["_update_lock" ]
205+ # while this code can execute at the same time as _maybe_precompute()
206+ # is updating the __precompute or scale() is updating the __coords,
207+ # there is no requirement for consistency between __coords and
208+ # __precompute
209+ state = self .__dict__ .copy ()
226210 return state
227211
228212 def __setstate__ (self , state ):
229213 self .__dict__ .update (state )
230- self ._update_lock = RWLock ()
231214
232215 def __eq__ (self , other ):
233216 """Compare for equality two points with each-other.
234217
235218 Note: only points that lay on the same curve can be equal.
236219 """
237- try :
238- self ._update_lock .reader_acquire ()
239- if other is INFINITY :
240- return not self .__y or not self .__z
241- x1 , y1 , z1 = self .__x , self .__y , self .__z
242- finally :
243- self ._update_lock .reader_release ()
220+ x1 , y1 , z1 = self .__coords
221+ if other is INFINITY :
222+ return not y1 or not z1
244223 if isinstance (other , Point ):
245224 x2 , y2 , z2 = other .x (), other .y (), 1
246225 elif isinstance (other , PointJacobi ):
247- try :
248- other ._update_lock .reader_acquire ()
249- x2 , y2 , z2 = other .__x , other .__y , other .__z
250- finally :
251- other ._update_lock .reader_release ()
226+ x2 , y2 , z2 = other .__coords
252227 else :
253228 return NotImplemented
254229 if self .__curve != other .curve ():
@@ -289,14 +264,9 @@ def x(self):
289264 call x() and y() on the returned instance. Or call `scale()`
290265 and then x() and y() on the returned instance.
291266 """
292- try :
293- self ._update_lock .reader_acquire ()
294- if self .__z == 1 :
295- return self .__x
296- x = self .__x
297- z = self .__z
298- finally :
299- self ._update_lock .reader_release ()
267+ x , _ , z = self .__coords
268+ if z == 1 :
269+ return x
300270 p = self .__curve .p ()
301271 z = numbertheory .inverse_mod (z , p )
302272 return x * z ** 2 % p
@@ -310,14 +280,9 @@ def y(self):
310280 call x() and y() on the returned instance. Or call `scale()`
311281 and then x() and y() on the returned instance.
312282 """
313- try :
314- self ._update_lock .reader_acquire ()
315- if self .__z == 1 :
316- return self .__y
317- y = self .__y
318- z = self .__z
319- finally :
320- self ._update_lock .reader_release ()
283+ _ , y , z = self .__coords
284+ if z == 1 :
285+ return y
321286 p = self .__curve .p ()
322287 z = numbertheory .inverse_mod (z , p )
323288 return y * z ** 3 % p
@@ -328,37 +293,28 @@ def scale(self):
328293
329294 Modifies point in place, returns self.
330295 """
331- try :
332- self ._update_lock .reader_acquire ()
333- if self .__z == 1 :
334- return self
335- finally :
336- self ._update_lock .reader_release ()
337-
338- try :
339- self ._update_lock .writer_acquire ()
340- # scaling already scaled point is safe (as inverse of 1 is 1) and
341- # quick so we don't need to optimise for the unlikely event when
342- # two threads hit the lock at the same time
343- p = self .__curve .p ()
344- z_inv = numbertheory .inverse_mod (self .__z , p )
345- zz_inv = z_inv * z_inv % p
346- self .__x = self .__x * zz_inv % p
347- self .__y = self .__y * zz_inv * z_inv % p
348- # we are setting the z last so that the check above will return
349- # true only after all values were already updated
350- self .__z = 1
351- finally :
352- self ._update_lock .writer_release ()
296+ x , y , z = self .__coords
297+ if z == 1 :
298+ return self
299+
300+ # scaling is deterministic, so even if two threads execute the below
301+ # code at the same time, they will set __coords to the same value
302+ p = self .__curve .p ()
303+ z_inv = numbertheory .inverse_mod (z , p )
304+ zz_inv = z_inv * z_inv % p
305+ x = x * zz_inv % p
306+ y = y * zz_inv * z_inv % p
307+ self .__coords = (x , y , 1 )
353308 return self
354309
355310 def to_affine (self ):
356311 """Return point in affine form."""
357- if not self .__y or not self .__z :
312+ _ , y , z = self .__coords
313+ if not y or not z :
358314 return INFINITY
359315 self .scale ()
360- # after point is scaled, it's immutable, so no need to perform locking
361- return Point (self .__curve , self . __x , self . __y , self .__order )
316+ x , y , z = self . __coords
317+ return Point (self .__curve , x , y , self .__order )
362318
363319 @staticmethod
364320 def from_affine (point , generator = False ):
@@ -423,17 +379,13 @@ def _double(self, X1, Y1, Z1, p, a):
423379
424380 def double (self ):
425381 """Add a point to itself."""
426- if not self .__y :
382+ X1 , Y1 , Z1 = self .__coords
383+
384+ if not Y1 :
427385 return INFINITY
428386
429387 p , a = self .__curve .p (), self .__curve .a ()
430388
431- try :
432- self ._update_lock .reader_acquire ()
433- X1 , Y1 , Z1 = self .__x , self .__y , self .__z
434- finally :
435- self ._update_lock .reader_release ()
436-
437389 X3 , Y3 , Z3 = self ._double (X1 , Y1 , Z1 , p , a )
438390
439391 if not Y3 or not Z3 :
@@ -546,16 +498,9 @@ def __add__(self, other):
546498 raise ValueError ("The other point is on different curve" )
547499
548500 p = self .__curve .p ()
549- try :
550- self ._update_lock .reader_acquire ()
551- X1 , Y1 , Z1 = self .__x , self .__y , self .__z
552- finally :
553- self ._update_lock .reader_release ()
554- try :
555- other ._update_lock .reader_acquire ()
556- X2 , Y2 , Z2 = other .__x , other .__y , other .__z
557- finally :
558- other ._update_lock .reader_release ()
501+ X1 , Y1 , Z1 = self .__coords
502+ X2 , Y2 , Z2 = other .__coords
503+
559504 X3 , Y3 , Z3 = self ._add (X1 , Y1 , Z1 , X2 , Y2 , Z2 , p )
560505
561506 if not Y3 or not Z3 :
@@ -603,7 +548,7 @@ def _naf(mult):
603548
604549 def __mul__ (self , other ):
605550 """Multiply point by an integer."""
606- if not self .__y or not other :
551+ if not self .__coords [ 1 ] or not other :
607552 return INFINITY
608553 if other == 1 :
609554 return self
@@ -615,8 +560,7 @@ def __mul__(self, other):
615560 return self ._mul_precompute (other )
616561
617562 self = self .scale ()
618- # once scaled, point is immutable, not need to lock
619- X2 , Y2 = self .__x , self .__y
563+ X2 , Y2 , _ = self .__coords
620564 X3 , Y3 , Z3 = 0 , 0 , 1
621565 p , a = self .__curve .p (), self .__curve .a ()
622566 _double = self ._double
@@ -664,11 +608,10 @@ def mul_add(self, self_mul, other, other_mul):
664608
665609 # as we have 6 unique points to work with, we can't scale all of them,
666610 # but do scale the ones that are used most often
667- # (post scale() points are immutable so no need for locking)
668611 self .scale ()
669- X1 , Y1 , Z1 = self .__x , self . __y , self . __z
612+ X1 , Y1 , Z1 = self .__coords
670613 other .scale ()
671- X2 , Y2 , Z2 = other .__x , other . __y , other . __z
614+ X2 , Y2 , Z2 = other .__coords
672615
673616 _double = self ._double
674617 _add = self ._add
@@ -736,13 +679,8 @@ def mul_add(self, self_mul, other, other_mul):
736679
737680 def __neg__ (self ):
738681 """Return negated point."""
739- try :
740- self ._update_lock .reader_acquire ()
741- return PointJacobi (
742- self .__curve , self .__x , - self .__y , self .__z , self .__order
743- )
744- finally :
745- self ._update_lock .reader_release ()
682+ x , y , z = self .__coords
683+ return PointJacobi (self .__curve , x , - y , z , self .__order )
746684
747685
748686class Point (object ):
0 commit comments