Skip to content

Instantly share code, notes, and snippets.

@zerowidth
Last active December 13, 2015 20:48
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save zerowidth/4972131 to your computer and use it in GitHub Desktop.
(ns clj-shared-resource.coordinated)
; define a few helper functions
(defn thread-id []
(.getId (Thread/currentThread)))
; because stdout isn't synchronized:
(def logger (agent nil))
(defn log [& msgs]
(send-off logger (fn [_] (apply println (cons ";" msgs)))))
(def app {:started (promise)
:starting (atom nil)})
(defn start-app []
(let [tid (thread-id)]
(Thread/sleep (rand 500))
(deliver
(:started app)
(fn [n]
(str "app started in thread " tid " handled request " n)))))
(defn start-or-wait-for-app []
(let [me (thread-id)]
(if (= me (swap! (:starting app) (fn [id] (or id me))))
(do
(log me " starting app")
(let [started (start-app)]
(deliver (:started app) started)
(deref (:started app))))
(do
(log me " waiting")
(deref (:started app))))))
(defn get-app []
(if (realized? (:started app))
(deref (:started app))
(start-or-wait-for-app)))
(defn make-request
"start and call the shared application"
[request-number]
(log "request " request-number " retrieving app from " (thread-id))
(let [instance (get-app)]
(log (instance request-number))))
; have the logger sleep for a second so output isn't interleaved
(send-off logger (fn [_] (Thread/sleep 1000)))
(->>
(range 1 4)
(map (fn [n] (future-call #(make-request n))))
(map deref))
(Thread/sleep 600)
(make-request 4)
; wait for the logger to collect the log output
(await-for 1000 logger)
require "celluloid"
class App
def initialize(instance)
@instance = instance
end
def call(n)
"response #{n} from app #{@instance}"
end
end
class Server
include Celluloid
def get_app(thread_id)
if @app
puts "thread #{thread_id}: app started already"
else
if @starting
puts "thread #{thread_id}: waiting for app to start"
wait :started
else
@starting = true
puts "thread #{thread_id}: starting app"
sleep rand
puts "thread #{thread_id}: started app"
@app = App.new thread_id
signal :started
end
end
@app
end
end
server = Server.new
3.times.map do |n|
Thread.new do
puts server.get_app(Thread.current.object_id % 1000).call(n)
end
end.map(&:join)
sleep 1
puts server.get_app("main").call 3
(ns clj-shared-resource.naive)
(defn thread-id []
(.getId (Thread/currentThread)))
; because stdout isn't synchronized:
(def logger (agent nil))
(defn log [& msgs]
(send-off logger (fn [_] (apply println (cons ";" msgs)))))
; define the shared app
(def app (ref nil))
; naive check-or-set
(defn get-app []
(let [tid (thread-id)]
(if-let [instance @app]
(do
(log "thread " tid " got running app")
instance)
(do
(log "thread " tid " starting app")
(Thread/sleep (rand 500))
(log "thread " tid " started")
(dosync
(ref-set
app
(fn [n]
(str "app started in thread " tid " handled request " n))))))))
(defn make-request
"start and call the shared application"
[request-number]
(log "request " request-number " retrieving app from " (thread-id))
(let [instance (get-app)]
(log (instance request-number))))
; have the logger sleep for a second so output isn't interleaved
(send-off logger (fn [_] (Thread/sleep 1000)))
(->>
(range 1 4)
(map (fn [n] (future-call #(make-request n))))
(map deref))
(Thread/sleep 600)
(make-request 4)
; wait for the logger to collect the log output
(await-for 1000 logger)
require "celluloid"
class App
def initialize(instance)
@instance = instance
end
def call(n)
"response #{n} from app #{@instance}"
end
end
class Server
include Celluloid
# The thread this actor uses for execution doesn't change, so pass the calling
# thread id in so it can be logged instead.
def get_app(thread_id)
if @app
puts "thread #{thread_id}: app started already"
else
puts "thread #{thread_id}: starting app"
sleep rand
puts "thread #{thread_id}: started app"
@app = App.new thread_id
end
@app
end
end
server = Server.new
3.times.map do |n|
Thread.new do
puts server.get_app(Thread.current.object_id % 1000).call(n)
end
end.map(&:join)
sleep 1
puts server.get_app("main").call 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment