forked from crystal-lang/crystal
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: mt-safe runnables queue scheduler + fiber.resumeable (x86_64 only)
- Loading branch information
1 parent
b3cfc35
commit b067a0f
Showing
4 changed files
with
207 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# :nodoc: | ||
class Crystal::Scheduler | ||
# :nodoc: | ||
# | ||
# Crystal only accepts simple primitives such as Atomic(T). This struct wraps | ||
# a 64-bit struct as an Atomic(UInt64) and transparently casts from and to | ||
# UInt64 and T. | ||
struct AtomicRef8(T) | ||
def initialize(value : T) | ||
# TODO: raise a compile-time error unless sizeof(T) == 8 | ||
@atomic = Atomic(UInt64).new(value.unsafe_as(UInt64)) | ||
end | ||
|
||
def get : T | ||
@atomic.get.unsafe_as(T) | ||
end | ||
|
||
def set(value : T) | ||
@atomic.set(value.unsafe_as(UInt64)) | ||
end | ||
|
||
def compare_and_set(cmp : T, new : T) : Bool | ||
_, success = @atomic.compare_and_set(cmp.unsafe_as(UInt64), new.as(UInt64)) | ||
success | ||
end | ||
end | ||
|
||
# :nodoc: | ||
# | ||
# Thread-safe non-blocking queue for work-stealing schedulers. | ||
# | ||
# Based on: | ||
# | ||
# - "Scheduling Multithreaded Computations by Work Stealing" (2001) by | ||
# Nimar S. Arora, Robert D. Blumofe and C. Greg Plaxton. | ||
# | ||
# - "Verification of a Concurrent Deque Implementation" (1999) by | ||
# Robert D. Blumofe, C. Greg Plaxton and Sandip Ray. | ||
struct Queue(T) | ||
# :nodoc: | ||
record Age, tag : Int32, top : Int32 | ||
|
||
# :nodoc: | ||
SIZE = 4 * 1024 * 1024 * 1024 | ||
|
||
def initialize | ||
@bot = Atomic(Int32).new(0) | ||
@age = AtomicRef8(Age).new(Age.new(0, 0)) | ||
|
||
prot = LibC::PROT_READ | LibC::PROT_WRITE | ||
flags = LibC::MAP_ANONYMOUS | LibC::MAP_PRIVATE | ||
buf = LibC.mmap(nil, SIZE, prot, flags, -1, 0) | ||
raise Errno.new("mmap") if buf == LibC::MAP_FAILED | ||
@deq = buf | ||
end | ||
|
||
def free | ||
LibC.munmap(@deq, SIZE) | ||
end | ||
|
||
# Pushes an item to the tail of the queue. Not thread-safe and must be | ||
# called from the thread that owns the queue. | ||
def push_bottom(item : T) : Nil | ||
bot = @bot.get | ||
@deq[bot] = item | ||
@bot.set(bot + 1) | ||
end | ||
|
||
# Pops an item from the tail of the queue. Not thread-safe and must be | ||
# called from the thread that owns the queue. | ||
def pop_bottom : T? | ||
bot = @bot.get | ||
return if bot == 0 | ||
|
||
bot -= 1 | ||
@bot.set(bot) | ||
|
||
item = @deq[bot] | ||
old_age = @age.get | ||
return item if bot > old_age.top | ||
|
||
@bot.set(0) | ||
new_age = Age.new(old_age.tag + 1, 0) | ||
|
||
if bot == old_age.top | ||
_, success = @age.compare_and_set(old_age, new_age) | ||
return item if success | ||
end | ||
|
||
@age.set(new_age) | ||
nil | ||
end | ||
|
||
# Pops an item from the head of the queue. Thread-safe and should be called | ||
# from threads that don't own the queue (i.e. stealing threads). | ||
def pop_top : T? | ||
old_age = @age.get | ||
bot = @bot.get | ||
return if bot <= old_age.top | ||
|
||
item = @seq[old_age.top] | ||
new_age = Age.new(old_age.tag, old_age.top + 1) | ||
|
||
_, success = @age.compare_and_set(old_age, new_age) | ||
return item if success | ||
end | ||
|
||
# Lazily returns the queue size. It may be equal or slightly more or less | ||
# than the actual size. | ||
def lazy_size : Int32 | ||
bot = @bot.get | ||
age = @age.get | ||
bot - age.top | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters