-
-
Notifications
You must be signed in to change notification settings - Fork 925
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add property and non-volatile table-based ivars.
This is in prep for officially saying we don't guarantee that ivarrs will be volatile. Users concerned about this are recommended to look into concurrent-ruby, which provides better guarantees and utilities for volatility, atomicity, and more. Note that there may still be a chance that other volatile accesses around the variable updates force it to be effectively volatile, but this will not be the case when reifying variables to be Java fields, as we hope to do in an upcoming release. See discussions surrounding ruby-concurrency/concurrent-ruby#422 for more details. See also #3353 for discussion about get volatility prior to this upcoming change in policy.
- 9.4.12.0
- 9.4.11.0
- 9.4.10.0
- 9.4.9.0
- 9.4.8.0
- 9.4.7.0
- 9.4.6.0
- 9.4.5.0
- 9.4.4.0
- 9.4.3.0
- 9.4.2.0
- 9.4.1.0
- 9.4.0.0
- 9.3.15.0
- 9.3.14.0
- 9.3.13.0
- 9.3.12.0
- 9.3.11.0
- 9.3.10.0
- 9.3.9.0
- 9.3.8.0
- 9.3.7.0
- 9.3.6.0
- 9.3.5.0
- 9.3.4.0
- 9.3.3.0
- 9.3.2.0
- 9.3.1.0
- 9.3.0.0
- 9.2.21.0
- 9.2.20.1
- 9.2.20.0
- 9.2.19.0
- 9.2.18.0
- 9.2.17.0
- 9.2.16.0
- 9.2.15.0
- 9.2.14.0
- 9.2.13.0
- 9.2.12.0
- 9.2.11.1
- 9.2.11.0
- 9.2.10.0
- 9.2.9.0
- 9.2.8.0
- 9.2.7.0
- 9.2.6.0
- 9.2.5.0
- 9.2.4.1
- 9.2.4.0
- 9.2.3.0
- 9.2.2.0
- 9.2.1.0
- 9.2.0.0
- 9.1.17.0
- 9.1.16.0
- 9.1.15.0
- 9.1.14.0
- 9.1.13.0
- 9.1.12.0
- 9.1.11.0
- 9.1.10.0
- 9.1.9.0
- 9.1.8.0
- 9.1.7.0
- 9.1.6.0
- 9.1.5.0
- 9.1.4.0
- 9.1.3.0
- 9.1.2.0
- 9.1.1.0
- 9.1.0.0
- 9.0.5.0
Showing
3 changed files
with
174 additions
and
3 deletions.
There are no files selected for viewing
160 changes: 160 additions & 0 deletions
160
core/src/main/java/org/jruby/runtime/ivars/NonvolatileVariableAccessor.java
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,160 @@ | ||
/* | ||
***** BEGIN LICENSE BLOCK ***** | ||
* Version: EPL 1.0/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Eclipse Public | ||
* License Version 1.0 (the "License"); you may not use this file | ||
* except in compliance with the License. You may obtain a copy of | ||
* the License at http://www.eclipse.org/legal/epl-v10.html | ||
* | ||
* Software distributed under the License is distributed on an "AS | ||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* rights and limitations under the License. | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either of the GNU General Public License Version 2 or later (the "GPL"), | ||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the EPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the EPL, the GPL or the LGPL. | ||
***** END LICENSE BLOCK *****/ | ||
package org.jruby.runtime.ivars; | ||
|
||
import org.jruby.RubyBasicObject; | ||
import org.jruby.RubyClass; | ||
import org.jruby.util.unsafe.UnsafeHolder; | ||
|
||
/** | ||
* A VariableAccessor that directly updates instance variables without an explicit memory fence | ||
* or synchronization. | ||
* | ||
* If the table itself must be created for the first time or grown to accommodate a new variable, | ||
* those operations will be done in a thread-safe (volatile, atomic) way. However updates of an | ||
* entry in an existing table will not have any explicit memory fence or synchronization. | ||
*/ | ||
public class NonvolatileVariableAccessor extends VariableAccessor { | ||
/** | ||
* Construct a new NonvolatileVariableAccessor for the given "real" class, | ||
* variable name, variable index, and class ID. | ||
* | ||
* @param realClass the "real" class | ||
* @param name the variable's name | ||
* @param index the variable's index | ||
* @param classId the class's ID | ||
*/ | ||
public NonvolatileVariableAccessor(RubyClass realClass, String name, int index, int classId) { | ||
super(realClass, name, index, classId); | ||
} | ||
|
||
/** | ||
* Set this variable into the given object using Unsafe to ensure | ||
* safe creation or growth of the variable table. | ||
* | ||
* @param object the object into which to set this variable | ||
* @param value the variable's value | ||
*/ | ||
public void set(Object object, Object value) { | ||
((RubyBasicObject)object).ensureInstanceVariablesSettable(); | ||
setVariable((RubyBasicObject)object, realClass, index, value); | ||
} | ||
|
||
/** | ||
* Set the given variable index into the specified object. The "real" class | ||
* and index are pass in to provide functional access. This version checks | ||
* if self has been frozen before proceeding to set the variable. | ||
* | ||
* @param self the object into which to set the variable | ||
* @param realClass the "real" class for the object | ||
* @param index the index of the variable | ||
* @param value the variable's value | ||
*/ | ||
public static void setVariableChecked(RubyBasicObject self, RubyClass realClass, int index, Object value) { | ||
self.ensureInstanceVariablesSettable(); | ||
setVariable(self, realClass, index, value); | ||
} | ||
|
||
/** | ||
* Set the given variable index into the specified object. The "real" class | ||
* and index are pass in to provide functional access. | ||
* | ||
* @param self the object into which to set the variable | ||
* @param realClass the "real" class for the object | ||
* @param index the index of the variable | ||
* @param value the variable's value | ||
*/ | ||
public static void setVariable(RubyBasicObject self, RubyClass realClass, int index, Object value) { | ||
while (true) { | ||
int currentStamp = self.varTableStamp; | ||
// spin-wait if odd | ||
if((currentStamp & 0x01) != 0) | ||
continue; | ||
|
||
Object[] currentTable = (Object[]) UnsafeHolder.U.getObjectVolatile(self, RubyBasicObject.VAR_TABLE_OFFSET); | ||
|
||
if (currentTable == null || index >= currentTable.length) { | ||
if (!createTableUnsafe(self, currentStamp, realClass, currentTable, index, value)) continue; | ||
} else { | ||
if (!updateTable(self, currentStamp, currentTable, index, value)) continue; | ||
} | ||
|
||
break; | ||
} | ||
} | ||
|
||
/** | ||
* Create or exapand a table for the given object, using Unsafe CAS and | ||
* ordering operations to ensure visibility. | ||
* | ||
* @param self the object into which to set the variable | ||
* @param currentStamp the current variable table stamp | ||
* @param realClass the "real" class for the object | ||
* @param currentTable the current table | ||
* @param index the index of the variable | ||
* @param value the variable's value | ||
* @return whether the update was successful, for CAS retrying | ||
*/ | ||
private static boolean createTableUnsafe(RubyBasicObject self, int currentStamp, RubyClass realClass, Object[] currentTable, int index, Object value) { | ||
// try to acquire exclusive access to the varTable field | ||
if (!UnsafeHolder.U.compareAndSwapInt(self, RubyBasicObject.STAMP_OFFSET, currentStamp, ++currentStamp)) { | ||
return false; | ||
} | ||
|
||
Object[] newTable = new Object[realClass.getVariableTableSizeWithExtras()]; | ||
|
||
if(currentTable != null) { | ||
System.arraycopy(currentTable, 0, newTable, 0, currentTable.length); | ||
} | ||
|
||
newTable[index] = value; | ||
|
||
UnsafeHolder.U.putOrderedObject(self, RubyBasicObject.VAR_TABLE_OFFSET, newTable); | ||
|
||
// release exclusive access | ||
self.varTableStamp = currentStamp + 1; | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Update the given table table directly. | ||
* | ||
* @param self the object into which to set the variable | ||
* @param currentStamp the current variable table stamp | ||
* @param currentTable the current table | ||
* @param index the index of the variable | ||
* @param value the variable's value | ||
* @return whether the update was successful, for CAS retrying | ||
*/ | ||
private static boolean updateTable(RubyBasicObject self, int currentStamp, Object[] currentTable, int index, Object value) { | ||
currentTable[index] = value; | ||
|
||
// validate stamp. redo on concurrent modification | ||
return self.varTableStamp == currentStamp; | ||
} | ||
} |
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