Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrent posible problem in function get-proc #7

Closed
fgerard opened this issue Apr 8, 2014 · 2 comments
Closed

Concurrent posible problem in function get-proc #7

fgerard opened this issue Apr 8, 2014 · 2 comments

Comments

@fgerard
Copy link

fgerard commented Apr 8, 2014

I think I am having a memory leak an testing the usage of get-proc I think it should use "locking" like so:

(defn get-proc
  "Returns the Saxon Processor object, the thread-safe generator class for documents, 
  stylesheets, & XPaths. Creates & defs the Processor if not already created."
  {:tag Processor}
  []
  (locking get-proc
    (defonce ^:private p 
      (Processor. false)) 
    p))

This is because in a very heavy threading environment this can happen (example):

(defn f1 [m]
  (locking f1
    (println "Entrando a " m)
    (Thread/sleep 5000)
    (println "saliendo de " m)
    m))

(defn getx [] ; similar to get-proc
  (defonce ^:private c
    (f1 (rand-int 100)))
  c)

(doseq [n (range 0 3)]
  (future (getx)))

; you will see that f1 is invoked 3 times!

Best regards and thank you for this very helpful library.

Saludos

@pjt
Copy link
Owner

pjt commented Apr 9, 2014

Thanks for catching this race condition & providing such a helpful reproducing example.

There are two ways I'm inclined to solve this. The first is a variant of your proposal, just a little more idiomatic: use delay & force for lazy initialization like so:

(def ^:private proc 
  (delay (Processor. false)))

(defn get-proc []
  (force proc))

This is a variant of your proposal because Delay objects get dereferenced through a call to a synchronized method, so calling (force proc) serializes access to the Processor constructor like your use of locking does.

The second way to solve this is not to attempt lazy initialization at all. Doing so was probably premature optimization.

Can I ask, do you happen to favor one or the other?

@fgerard
Copy link
Author

fgerard commented Apr 9, 2014

I would go for just using defonce and omitting the laziness after all, you
are using the library right :)

On Tuesday, April 8, 2014, Perry Trolard notifications@github.com wrote:

Thanks for catching this race condition & providing such a helpful
reproducing example.

There are two ways I'm inclined to solve this. The first is a variant of
your proposal, just a little more idiomatic: use delay & force for lazy
initialization like so:

(def ^:private proc
(delay (Processor. false)))
(defn get-proc [](force proc))

This is a variant of your proposal because Delay objects get dereferenced
through a call to a synchronized method, so calling (force proc)serializes access to the Processor constructor like your use of
locking does.

The second way to solve this is not to attempt lazy initialization at all.
Doing so was probably premature optimization.

Can I ask, do you happen to favor one or the other?

Reply to this email directly or view it on GitHubhttps://github.com//issues/7#issuecomment-39926455
.

[image: fgerard.jpg]

pjt added a commit that referenced this issue Apr 21, 2014
This is to avoid the race condition related to `defonce` initialization,
& closes issue #7. Thanks to @fgerard for the report.
@pjt pjt closed this as completed Apr 21, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants