File: jruby_locking_test.clj

package info (click to toggle)
jruby-utils-clojure 5.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 544 kB
  • sloc: java: 768; makefile: 22; sh: 21; xml: 10; ruby: 3
file content (157 lines) | stat: -rw-r--r-- 6,969 bytes parent folder | download | duplicates (2)
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
(ns puppetlabs.services.jruby-pool-manager.jruby-locking-test
  (:require [clojure.test :refer :all]
            [puppetlabs.services.jruby-pool-manager.jruby-testutils :as jruby-testutils]
            [schema.test :as schema-test]
            [puppetlabs.services.jruby-pool-manager.jruby-core :as jruby-core])
  (:import (java.util.concurrent TimeoutException)))

(use-fixtures :once schema-test/validate-schemas)

(defn jruby-test-config
  [pool-size]
  (jruby-testutils/jruby-config {:max-active-instances pool-size
                                 :borrow-timeout 1}))

(defn can-borrow-from-different-thread?
  [pool-context]
  @(future
    (if-let [instance (jruby-core/borrow-from-pool-with-timeout pool-context :test [])]
      (do
        (jruby-core/return-to-pool pool-context instance :test [])
        true))))

(deftest ^:integration with-lock-test
  (jruby-testutils/with-pool-context
   pool-context
   jruby-testutils/default-services
   (jruby-test-config 1)
   (jruby-testutils/wait-for-jrubies-from-pool-context pool-context)
   (testing "initial state of write lock is unlocked"
     (is (can-borrow-from-different-thread? pool-context))
     (testing "with-lock macro holds write lock while executing body"
       (jruby-core/with-lock
        pool-context
        :with-lock-holds-lock-test
        (is (not (can-borrow-from-different-thread? pool-context)))))
     (testing "with-lock macro releases write lock after exectuing body"
       (is (can-borrow-from-different-thread? pool-context))))))

(deftest ^:integration with-lock-exception-test
  (jruby-testutils/with-pool-context
   pool-context
   jruby-testutils/default-services
   (jruby-test-config 1)
   (jruby-testutils/wait-for-jrubies-from-pool-context pool-context)
   (testing "initial state of write lock is unlocked"
     (is (can-borrow-from-different-thread? pool-context)))

   (testing "with-lock macro releases lock even if body throws exception"
     (is (thrown? IllegalStateException
                  (jruby-core/with-lock pool-context :with-lock-exception-test
                                        (is (not (can-borrow-from-different-thread?
                                                  pool-context)))
                                        (throw (IllegalStateException. "exception")))))
     (is (can-borrow-from-different-thread? pool-context)))))

(deftest ^:integration with-lock-event-notification-test
  (testing "locking sends event notifications"
    (let [events (atom [])
          callback (fn [{:keys [type]}]
                     (swap! events conj type))]
      (jruby-testutils/with-pool-context
        pool-context
        jruby-testutils/default-services
        (jruby-test-config 1)
        (jruby-testutils/wait-for-jrubies-from-pool-context pool-context)
        (jruby-core/register-event-handler pool-context callback)

        (testing "locking events trigger event notifications"
          (jruby-core/with-jruby-instance
           jruby-instance
           pool-context
           :with-lock-events-test
           (testing "borrowing a jruby triggers 'requested'/'borrow' events"
             (is (= [:instance-requested :instance-borrowed] @events))))
          (testing "returning a jruby triggers 'returned' event"
            (is (= [:instance-requested :instance-borrowed :instance-returned] @events)))
          (jruby-core/with-lock
           pool-context
           :with-lock-events-test
           (testing "acquiring a lock triggers 'lock-requested'/'lock-acquired' events"
             (is (= [:instance-requested :instance-borrowed :instance-returned
                     :lock-requested :lock-acquired] @events)))))
        (testing "releasing the lock triggers 'lock-released' event"
          (is (= [:instance-requested :instance-borrowed :instance-returned
                  :lock-requested :lock-acquired :lock-released] @events)))))))

(deftest ^:integration with-lock-and-borrow-contention-test
  (testing "contention for instances with borrows and locking handled properly"
    (jruby-testutils/with-pool-context
     pool-context
     jruby-testutils/default-services
     (jruby-test-config 2)
     (jruby-testutils/wait-for-jrubies-from-pool-context pool-context)
     (let [instance (jruby-core/borrow-from-pool-with-timeout
                     pool-context
                     :with-lock-and-borrow-contention-test
                     [])
           lock-acquired? (promise)
           unlock-thread? (promise)
           lock-thread (future (jruby-core/with-lock
                                pool-context
                                :with-lock-and-borrow-contention-test
                                (deliver lock-acquired? true)
                                @unlock-thread?))]
       (testing "lock not granted yet when instance still borrowed"
         (is (not (realized?
                   lock-acquired?))))
       (jruby-core/return-to-pool pool-context instance :with-lock-and-borrow-contention-test [])
       @lock-acquired?
       (testing "cannot borrow from non-locking thread when locked"
         (is (not (can-borrow-from-different-thread? pool-context))))
       (deliver unlock-thread? true)
       @lock-thread
       (testing "can borrow from non-locking thread after lock released"
         (is (can-borrow-from-different-thread? pool-context)))))))

(deftest ^:integration with-lock-with-timeout-test
  (testing "can obtain lock when timeout is not exceeded"
    (jruby-testutils/with-pool-context
     pool-context
     jruby-testutils/default-services
     (jruby-test-config 1)
     (let [pool (jruby-core/get-pool pool-context)]
       (jruby-core/with-lock-with-timeout
        pool-context
        10000000
        :lock-with-timeout-test
        (is (.isLocked pool)))
       (is (not (.isLocked pool))))))

  (testing "TimeoutException thrown when lock timeout is exceeded"
    (jruby-testutils/with-pool-context
     pool-context
     jruby-testutils/default-services
     (jruby-test-config 1)
     (let [pool (jruby-core/get-pool pool-context)
           borrowed-instance (jruby-core/borrow-from-pool
                              pool-context
                              :lock-timeout-exceeded-test
                              [])]
       (try
         ; Since an instance has been borrowed the lock won't be granted and should
         ; trigger the timeout immediately
         (is (thrown-with-msg?
              TimeoutException
              #"Timeout limit reached before lock could be granted"
              (jruby-core/with-lock-with-timeout
               pool-context
               1
               :lock-timeout-exceeded-test
               ; should not reach here
               (is false))))
         (is (not (.isLocked pool)))
         (finally
           (jruby-core/return-to-pool pool-context borrowed-instance
                                      :lock-timeout-exceeded-test
                                      [])))))))