Import OpenSSL-1.0.1h.
[dragonfly.git] / crypto / openssl / crypto / bn / bn_mont.c
index 427b5cf..ee8532c 100644 (file)
@@ -478,32 +478,38 @@ BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to, BN_MONT_CTX *from)
 BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, int lock,
                                        const BIGNUM *mod, BN_CTX *ctx)
        {
-       int got_write_lock = 0;
        BN_MONT_CTX *ret;
 
        CRYPTO_r_lock(lock);
-       if (!*pmont)
+       ret = *pmont;
+       CRYPTO_r_unlock(lock);
+       if (ret)
+               return ret;
+
+       /* We don't want to serialise globally while doing our lazy-init math in
+        * BN_MONT_CTX_set. That punishes threads that are doing independent
+        * things. Instead, punish the case where more than one thread tries to
+        * lazy-init the same 'pmont', by having each do the lazy-init math work
+        * independently and only use the one from the thread that wins the race
+        * (the losers throw away the work they've done). */
+       ret = BN_MONT_CTX_new();
+       if (!ret)
+               return NULL;
+       if (!BN_MONT_CTX_set(ret, mod, ctx))
                {
-               CRYPTO_r_unlock(lock);
-               CRYPTO_w_lock(lock);
-               got_write_lock = 1;
+               BN_MONT_CTX_free(ret);
+               return NULL;
+               }
 
-               if (!*pmont)
-                       {
-                       ret = BN_MONT_CTX_new();
-                       if (ret && !BN_MONT_CTX_set(ret, mod, ctx))
-                               BN_MONT_CTX_free(ret);
-                       else
-                               *pmont = ret;
-                       }
+       /* The locked compare-and-set, after the local work is done. */
+       CRYPTO_w_lock(lock);
+       if (*pmont)
+               {
+               BN_MONT_CTX_free(ret);
+               ret = *pmont;
                }
-       
-       ret = *pmont;
-       
-       if (got_write_lock)
-               CRYPTO_w_unlock(lock);
        else
-               CRYPTO_r_unlock(lock);
-               
+               *pmont = ret;
+       CRYPTO_w_unlock(lock);
        return ret;
        }