The Rubinius Language Platform
MPL-2.0 License
Bot releases are hidden (Show)
Published by brixen about 8 years ago
Version 3.48 (2016-07-20)
However, constants are odd things in Ruby: their values are neither constant
nor is their lookup restricted to one scope. Instead, their values may be
reassigned and their lookup starts with the lexical scope but continues with
the inheritance chain.
With the additional of functions in Rubinius bound to lexical scope, it is
reasonable to refocus this object on its actual role: to represent a lexical
scope.
Published by brixen over 8 years ago
Version 3.47 (2016-07-15)
A linear scan list is still used for hash values that collide along (6 * 10 =
60 bits), but this could be enhanced to be a binary/red-black/etc tree
instead.
Published by brixen over 8 years ago
Version 3.46 (2016-07-12)
Published by brixen over 8 years ago
Version 3.45 (2016-07-10)
This is a complex part of Rubinius and FFI where we need some primitive
code to interact with libc macros.
Fixed +timeout+ handling so that a nil timeout is respected.
Prior to this we were allocating timeval structs every time and
it turns out that an empty struct and a nil value are not
equivalent in behavior.
Took the opportunity to refactor and DRY up some code that
validated IO.select arguments.
Turns out that #truncate needed to be a class method, so I moved
it to a class method of FileDescriptor.
Also added support for creating a DirectoryFileDescriptor. It
merely inherits from FileDescriptor now. We will see if it needs
any new behavior or if default behavior needs to be curtailed.
Tricky one to find. The original spec used #gets(1) to read one
char. Unfortunately, this arg format ends up setting a separator
to $/ so the logic ends up calling
EachReader#read_to_separator_with_limit instead of
EachReader#read_to_limit. By passing nil, as in #gets(nil, 1),
then we force the code paths to call read_to_limit.
This fixes several specs shared by gets, foreach, and readline.
The EachReader class needs a good refactoring at this point
but for now I just want to commit working code.
Also took the opportunity to refactor fcntl F_GETFL and F_SETFL
This is really weird. The code calls #find_type in the scope
of the enclosing_module, but there could be a conflict with
that method name, numbers of args it takes, its purpose, etc.
So a failure here should be rescued and allow the followup
code to call #find_type on the FFI class.
This was discovered when the ffi-io branch failed to build
a native extension. The mkmf.rb gem was doing some logging
during the build which caused some code in IO::Select to
call IO::Select.class_eval on code to instantiate the
Timeval_t struct class. Creating a struct calls #find_type
but it was calling it from the scope of mkmf.rb which had
its own (completely different!) method named #find_type
defined. It threw an ArgumentError because the arity didn't
match, but it lead me to this logic error and fix.
During a call to IO.reopen a file descriptor was dup2'ed. Its
ftype changed from "file" to "fifo" and as we know a fifo cannot
seek. So we were blowing up when trying to set the offset ivar.
By only allowing seek on "file" FDs, we'll avoid the bug.
As far as I can tell, there is no #buffer_empty? public method on
the IO class. We had one to test this behavior. Gone!
We had a failing spec where a thread blocked on read was
supposed to raise IOError when another thread closed
the IO. However, it didn't work. Turns out that the
low-level system read function blocks forever so the
closed IO/file descriptor didn't raise as expected.
The original C code got around this OS behavior by setting
special flags and sending signals to the blocked thread
to wake it up. This mechanism was only exposed to the
bytecode VM and wasn't available to the Ruby runtime so
I had to go this other direction. It's less than ideal
but if the long-term plan is to utilize libuv for
Rubinius IO then this is a good enough fix.
I don't really like this solution very much. Ideally the
Socket classes would set @fd directly in their #initialize
methods via a call to FileDescriptor.choose_type(fd). Having
a +fd+ accessor on the IO class was intended only for
debugging purposes.
Let's go with this patch for now. If the larger refactoring
of the IO class to use a private IO::FileDescriptor class
is accepted then we can go back and change the Socket methods
to conform better and remove this accessor.
The IO finalizer is tricky since it also has to detect possible
modifications from C API calls. There will likely need to be
some rework done on the C API code itself to make this easier
to manage from the Ruby side since we are trying to get as much
as possible out of C/C++.
This reverts commit ca755090dec70fa54ed43fbcc1585c24ff6c5522.
Re-introduced IO::open_with_cloexec to clean up this.
Since a SpinLock is a simple integer on which CAS operations are performed,
there is no way to go afoul of 'ownership' during fork(). This appears to
solve a spordic issue where the child was not able to reset the fork_exec_lock_
inherited from the parent process.
Before the code was looking up 'self' by getting the current
call frame and pulling self from it. This was returning a
different object than expected. So now we just call #send
against the current object; this works consistently.
We need to ensure this is fixed now.
This reverts commit 6455dc7590bbf9b0fb904846b115bb9cefb3ca01.
This reverts commit 39d255f5b72eadcce34f0deb2e83cd3c7254254c.
This reverts commit 679a247a3a932d3b22d8d20082b2026ea2b52cfa.
Published by brixen over 8 years ago
Version 3.44 (2016-07-10)
Published by brixen over 8 years ago
Version 3.43 (2016-07-01)
To meet this constraint, we pre-allocate the number of InlineCache slots
specified at process invocation for every CallSite object. Those slots point
to individual InlineCache objects as the call site is executed and the type
profile changes. The pointers are swapped atomically, and if the atomic swap
succeeds, the previous cache is retained in a 'dead list' until the next
garbage collection cycle. If the update fails, the updated cache is discarded
and the update is attempted again.
Nothing should retain the InlineCache pointers outside the CallSite object.
During execution of the CallSite, the InlineCache pointer should be on the
execution stack, so swapping the pointers should not impact code that is
already-in-progress. At a garbage collection checkpoint, no interpreter will
be in-progress, so deleting the replaced InlineCache objects should be safe.
Published by brixen over 8 years ago
Version 3.42 (2016-06-26)
When a Fiber that has not completed normally goes out of 'scope' (ie is no
longer reachable from any reachable object), the garbage collector cannot
simply reclaim the managed resources allocated to the Fiber because the Fiber
composes native resources (ie the pthread instance). In this case, the Fiber's
pthread invoke function needs to be forced to exit so that the pthread
instance can be reclaimed.
Published by brixen over 8 years ago
Version 3.41 (2016-06-20)
We attempt to make the threshold high enough to avoid spurious failures but
not high enough to lock up a process too long because a Thread may write to
the log when the process is trying to checkpoint for eg garbage collection.
Published by brixen over 8 years ago
Version 3.40 (2016-06-12)
Published by brixen over 8 years ago
Version 3.39 (2016-06-09)
The default is to filter the Ruby core library location. But when a utility
library is used, the location will always show the same utility library source
code. Adding a custom filter that excludes the library locations allows the
client source code location to be logged instead.
The following configuration options designate the source filters:
-Xmachine.thread.log.filter (default '^core/.$')
-Xmachine.fiber.log.filter (default '^core/.$')
-Xsystem.log.filter (default '^core/.*$')
The filter must match the entire source code line to exclude it. The first
non-matching source code line will be logged.
The filter expression uses the C++ regex facility and ECMAScript syntax.
This may cause ambiguity about what configuration options are in effect when a
process runs. The -Xsystem.log.config (default: yes) option prints the
configuration options that were parsed to the log file.
Published by brixen over 8 years ago
Version 3.38 (2016-06-09)
Fixed explicit mutex unlock straggler. (Brian Shirai)
This got left in when I was trying to get the mutex to properly unlock across
fork() calls in both parent and child process.
Fixed specs to properly wait for child processes. (Brian Shirai)
Fixed waiting for failed spawn, backtick children. (Brian Shirai)
Added config for logging Thread, Fiber events. (Brian Shirai)
The follow configuration options control whether to log lifetime events (eg create, exit)
for Thread and Fiber, and whether to log finalizer activity.
-Xmachine.thread.log.lifetime (default true)
-Xmachine.thread.log.finalizer (default false)
-Xmachine.fiber.log.lifetime (default true)
-Xmachine.fiber.log.finalizer (default false)
Recreate logger instance after fork(). (Brian Shirai)
During a fork() event, it's possible for another thread to be writing to the
logger and the logger mutex to be in an unknown state in the child. So, we
recreate the logger post fork() in the child. This also simplifies ensuring
that eg the PID stamp is correct.
Added config option for logging process lifetime events. (Brian Shirai)
During the process lifetime, various events like spawn, fork, exec, etc may be
logged. The following option allows turning off those events;
-Xsystem.log.lifetime
This may be useful to filter information from the logs when running
particularly noisy processes that do a lot of subprocess activity. The default
state, however, is to log these events because they can be extremely helpful
when debugging issue.
Published by brixen over 8 years ago
Version 3.37 (2016-06-06)
See http://petereisentraut.blogspot.co.il/2011/05/ccache-and-clang.html
We switch to a custom spinlock mutex implemented with C++11 features
(unfortunate that C++11 didn't think it was essential to provide such a mutex
but it's trivial to create).
Since the process may log something at any time from any thread, running
managed or unmanaged, even while fork'ing, we always reset the state of the
lock after fork().
Don't copy dead Fiber stack to heap. (Brian Shirai)
Either status_ is wrong and the Fiber isn't dead, in which case we run the risk of
losing its stack, or we should ignore the copy_to_heap.
Use sequentially consistent memory order for spinlock_mutex. (Brian Shirai)
Cleaned up passing STATE to logger. (Brian Shirai)
The logging facility is intentially highly decoupled from Rubinius so that it
can be used through the entire process lifetime without depending on
structures that need to be constructed and destructed. The STATE dependency
was added to try to synchronize logging across fork() calls, but that wasn't a
good architecture choice.
Only reset the logger lock in the fork child. (Brian Shirai)
Test if Travis failure is Fiber-related. (Brian Shirai)
Added debug logging for Data, Fiber finalize. (Brian Shirai)
Revert "Test if Travis failure is Fiber-related." (Brian Shirai)
This reverts commit 9f644d75e2662624f8787fa0a3916b10a80c283d.
Switched Data, Fiber to normal logging. (Brian Shirai)
Another Travis test. (Brian Shirai)
Improved logging message format. (Brian Shirai)
Generally, log messages for non-exceptional events should follow the format:
object: operation: details
For example: 'process: fork: child: <PID, ...'
For exceptional events, the format should be:
: object: details
The Rubinius log includes entries for the Fiber create and exit events.
Published by brixen over 8 years ago
Version 3.36 (2016-06-05)
Published by brixen over 8 years ago
Version 3.35 (2016-06-04)
Published by brixen over 8 years ago
Version 3.34 (2016-06-02)
Also, at every moment that a thread is possibly running managed code, it must
have the proper thread phase state, or it could modify code concurrently with
the garbage collector in a way that leads to corrupt objects or references
that are no longer valid.
Hence, we need a mechanism to hold all threads except the halting thread but
also allow that thread to run arbitrary managed code, one function of which is
running the garbage collection code, which needs to stop the world for some
things, which relies on the ThreadNexus synchronization code, and on and on.
So we use an atomic variable that holds the thread's id and if the variable
already has a thread's id, that thread is the one who set it, and hence can
proceed confident that no other thread is running.
. (Brian Shirai) If #spawn or #
fail, the PID of the subprocess isn't passed back, so itIn the child process, the only thread that persists is the thread that called
fork(). If another thread was about to write to the logger and got suspended
before unlocking the logger mutex, the mutex will be locked in the child and
will block any other thread that tries to lock it. The thread in the parent
that would have unlocked the lock won't exist in the child.
An alternative would be for the thread calling fork to hold all the relevant
locks before calling fork(), and then unlocking them all after fork() in the
child. Functionally, that wouldn't be any different and would impose
additional costs before the fork().
All code needs to be audited for any locks that could be held across a fork()
call and all those should be re-init'd in the child.
Published by brixen over 8 years ago
Version 3.33 (2016-05-23)
Thread.new, Thread.start, Thread.fork, and Fiber.new all take an optional
keyword argument of the form: 'stack_size: Fixnum', where Fixnum includes
objects that respond to #to_int. The keyword argument, if present, sets the
size of the Thread's or Fiber's stack size. If the argument is not present or
is zero, the default stack size, configured with -Xmachine.thread.stack_size
and -Xmachine.fiber.stack_size are used instead.
Added configuration for machine.thread.stack_size to set the default size of
Thread stacks. Also moved fiber.stack_size configuration under machine
section.
Also added configuration for machine.stack_cushion to set the size in bytes of
the cushion to leave at the stack end when checking for stack usage. This is
necessary because we check the stack usage before calling a method. The method
may use quite a bit of stack but we can't know this beforehand (hand-wavy
"can't" here). If we are near the end but not yet at the end, we could easily
exhaust the stack after calling the method and then segfault.
Published by brixen over 8 years ago
Version 3.32 (2016-05-23)
Published by brixen over 8 years ago
Version 3.31 (2016-05-16)
Removed rbxti. (Brian Shirai)
Removed instrumenting profiler. (Brian Shirai)
Fixed merge from master. (Brian Shirai)
Removed Ubuntu 12.04 from binary builds. (Brian Shirai)
Updated profiler gems versions. (Brian Shirai)
Updated to Bundler 1.12.3. (Brian Shirai)
Simply track MachineCode execution. (Brian Shirai)
Removed remnants of rbxti. (Brian Shirai)
Removed VMJIT, added profile interval to checkpoint. (Brian Shirai)
Added simple, per-thread, random interval sampling. (Brian Shirai)
This is mostly a temporary experiment that will likely be replaced with a
calling-context tree or similar structure and a separate profiler thread.
Added profile metrics. (Brian Shirai)
Don't block GC on profiler interval. (Brian Shirai)
Added more profiler functionality. (Brian Shirai)
Use -Xsystem.profiler.target= to set the output path for the profiler
report. The path can optionally contain the text $PID where desired and the
process ID will be substituted. Quoting the option may be necessary to prevent
the shell from expanding $PID prematurely. For example:
rbx -Xsystem.profiler.target='path/to/profiler_report-$PID.txt'
The fully qualified name is implemented yet because it requires an additional
data structure to properly track the components, so only the bare method name
is displayed.
Published by brixen over 8 years ago
Version 3.30 (2016-05-06)
The vm()->thread_state()->raise_reason() == cThreadKill except when
vm_thread_state primitive is called from the Thread#run ensure clause.
For some reason, these updates appear to be causing the failure of the
spec quarantined in b903c1f1989233f95d1d329eef53cd30a266ad91.
See cfd672a5373e2e16a97b43f01692fe28c8a9043a
Time.now
has aHowever, not running the GC synchronously when a memory threshold is reached
(eg when the Immix region has exhausted available chunks) means that heap
growth or spill over to another region (eg the Large Object Space) is
essential.
The growth is undesirable (because right now there is no compaction and growth
will continue unbounded) and the spill is especially undesirable because the
LOS collector is much less efficient than the Immix collector.
Since the marker thread is essentially racing the mutator threads, under high
allocation rates, the marker would be perpetually behind and a lot of growth
or spill would occur.
Eventually, we'll implement compaction, which will mitigate heap growth. But
that doesn't solve all the problems. To address these issues, we adhere to the
following constraints:
During the finalizer finish process, no new finalizers may be created.
Previously, we treated these to types quite differently. We used the '// slot'
notation when declaring the first type so that we could automatically generate
code to process the objects. The second type was usually somewhat consistent
but also ad hoc in many places.
Now we mostly use two macros (attr_accessor for the first type, and attr_field
for the second type) that both define the variables and define setters and
getters for them. There are still some places where this needs to be cleaned
up.
One reason that we need to be very careful about the setters for the first
type of variable (the managed object references) is that when the concurrent
GC is running, we need to know when an object reference is stored into an
object that may have already been processed by the marker. If we don't see
this, that object may be considered unreachable by the GC even though it's
being referenced. This results in the equivalent to a use-after-free bug in a
language with manually managed memory.
Consistently using the managed object reference accessors means that we can
layer other behavior on the access functions. For example, if we tag objects
with an identifier for the thread that created them, we can log or prevent
mutation access from a different thread.
=== 2.6.2 / 2016-03-12
Bug fixes:
=== 2.6.1 / 2016-02-28
Bug fixes:
default_path
and home
are set for paths. Pull request #1513Gem.paths=
. Pull=== 2.6.0 / 2016-02-26
Minor enhancements:
gem push
to the gem's "allowed_push_host"Bug fixes:
gem push
credentials under the host you signed-in for.coding
location to first line. Pull request #1471 by SHIBATAGem::Version@segments
instance variable. Pull request #1487 by=== 2.5.2 / 2016-01-31
Bug fixes:
gemspec
method, fixing #1204 and #1033. Pull requestMinor enhancements:
--no-rc
flag, which skips loading .gemrc
. Pull request #1329 by Luisallowed_push_host
. By Josh Lane.gem list --exact
, which finds gems by string match instead of regex. Pull--source
. Pull request--[no-]post-install-message
to install
and update
. Pull request #1162--host
option to yank
, providing symmetry with pull
. Pull requestbuild
without '.gemspec'. Pull request #1454 by Stephensource
option on gems in Gemfile. Pull request #1355 by=== 2.5.1 / 2015-12-10
Bug fixes:
When a value is stored into a managed object, we run a write barrier. This bit
of code serves two functions. First, it "remembers" the object being stored in
case the object it is being stored into is a mature object and the object
being stored is a young object. When the young collector runs, those
"remembered" objects are part of the roots for the young region being
collected. Second, if the object being stored into hasn't been traced yet, we
trace it, and we add the object being stored to the stack of objects to trace.
If the Tuple fields aren't initialized, we'll hit a garbage value when we try
to trace it. If we don't run the write barrier when we assign values, we have
the chance of losing one of those references. An alternative to "doubly
initializing" the Tuple instance is to run two loops. One that assigns values
and one that runs the write barrier on the entries. It should be apparent that
we haven't really saved anything by using this purported "optimization" of
creating the Tuple uninitialized (dirty).
This is a good lesson in optimizing. The best optimization is not running any
code at all, rather than making running code faster, if that is an option. It
usually will give greater return to rework code in a way that significant code
can be eliminated vs "optimized". In this case, we should get rid of Array and
Tuple in the middle of stuff like calling methods.
Ultimately, CallFrame needs to be split into separate frame types for managed
code, FFI, JIT, and native methods (ie C-API), and constructors should be
defined for every type.
Published by brixen over 8 years ago
Version 3.29 (2016-04-30)