-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
74 changed files
with
7,691 additions
and
2,133 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
|
||
KNOWN PROBLEMS | ||
-------------- | ||
|
||
- Incorrect LinkedHashMap Spliterator ordering in Android N. | ||
|
||
The implementation of LinkedHashMap's collection views' spliterators in | ||
Android Nougat (API levels 24 and 25) uses the wrong order (inconsistent | ||
with the iterators, which use the correct order), despite reporting | ||
Spliterator#ORDERED (however, the unordered HashMap spliterators are used). | ||
|
||
Since streamsupport 1.5.3 and later will by default delegate to the Android 7.x | ||
spliterators you'd be affected by this unordered behavior on Android 7.x unless | ||
you disable spliterator delegation altogether or you workaround this behavior | ||
on API level 24 and 25 as follows. | ||
|
||
You may use the following code to obtain a correctly ordered Spliterator: | ||
|
||
For a Collection view | ||
col = lhm.keySet(), col = lhm.entrySet() or col = lhm.values(), use | ||
|
||
Spliterator sp = java8.util.Spliterators.spliterator(col, c) where | ||
|
||
int c = Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | ||
|
||
for keySet() and entrySet() and | ||
|
||
int c = Spliterator.ORDERED | Spliterator.SIZED for values() | ||
|
||
to obtain a correctly ordered spliterator. Then, instead of | ||
StreamSupport.stream(col) or StreamSupport.parallelStream(col), use | ||
|
||
java8.util.stream.StreamSupport.stream(sp, false) | ||
|
||
to construct a (nonparallel) java8.util.stream.Stream from the Spliterator sp. | ||
|
||
Note that these workarounds are only suggested where lhm is a LinkedHashMap. | ||
Note further that everything works perfectly fine on API level 23 and below (if | ||
you don't plan to deploy on Android 7.x devices, you can ignore this whole advice | ||
altogether). | ||
|
||
To mitigate the risk if you don't follow this advice, streamsupport 1.5.3 (and | ||
later) exercises a check whether the spliterator for a HashMap collection view | ||
reports ORDERED (a bug) and excepts these cases from the delegation mechanism | ||
(using the reflective implementation instead. So, in effect the same mechanism | ||
that is used on API level 23 and below gets employed in this case). | ||
|
||
But note that this check isn't 100% fool-proof as the LinkedHashMap (or its | ||
collection view) could be wrapped, for example in a j.u.Collections$UnmodifiableMap | ||
(whose UnmodifiableEntrySetSpliterator delegates back to the defective | ||
HashMap$EntrySpliterator). | ||
|
||
Since we can't know an arbitrary wrapper beforehand there is nothing streamsupport | ||
can do about this in such cases - you have been warned. | ||
|
||
The latest version of LinkedHashMap in AOSP implements its Spliterators via | ||
Spliterators.spliterator(), which means that this bug has already been fixed | ||
in Android O. |
Large diffs are not rendered by default.
Oops, something went wrong.
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
2,875 changes: 1,303 additions & 1,572 deletions
2,875
src/alternative/java/java8/util/concurrent/ForkJoinPool.java
Large diffs are not rendered by default.
Oops, something went wrong.
203 changes: 203 additions & 0 deletions
203
src/alternative/java/java8/util/concurrent/ForkJoinWorkerThread.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,203 @@ | ||
/* | ||
* Written by Doug Lea with assistance from members of JCP JSR-166 | ||
* Expert Group and released to the public domain, as explained at | ||
* http://creativecommons.org/publicdomain/zero/1.0/ | ||
*/ | ||
package java8.util.concurrent; | ||
|
||
import java.security.AccessControlContext; | ||
import java.security.ProtectionDomain; | ||
|
||
/** | ||
* A thread managed by a {@link ForkJoinPool}, which executes | ||
* {@link ForkJoinTask}s. | ||
* This class is subclassable solely for the sake of adding | ||
* functionality -- there are no overridable methods dealing with | ||
* scheduling or execution. However, you can override initialization | ||
* and termination methods surrounding the main task processing loop. | ||
* If you do create such a subclass, you will also need to supply a | ||
* custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to | ||
* {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}. | ||
* | ||
* @since 1.7 | ||
* @author Doug Lea | ||
*/ | ||
public class ForkJoinWorkerThread extends Thread { | ||
// CVS rev. 1.73 | ||
/* | ||
* ForkJoinWorkerThreads are managed by ForkJoinPools and perform | ||
* ForkJoinTasks. For explanation, see the internal documentation | ||
* of class ForkJoinPool. | ||
* | ||
* This class just maintains links to its pool and WorkQueue. The | ||
* pool field is set immediately upon construction, but the | ||
* workQueue field is not set until a call to registerWorker | ||
* completes. This leads to a visibility race, that is tolerated | ||
* by requiring that the workQueue field is only accessed by the | ||
* owning thread. | ||
* | ||
* Support for (non-public) subclass InnocuousForkJoinWorkerThread | ||
* requires that we break quite a lot of encapsulation (via helper | ||
* methods in ThreadLocalRandom) both here and in the subclass to | ||
* access and set Thread fields. | ||
*/ | ||
|
||
// A placeholder name until a useful name can be set in registerWorker | ||
private static final String NAME_PLACEHOLDER = "aForkJoinWorkerThread"; | ||
|
||
final ForkJoinPool pool; // the pool this thread works in | ||
final ForkJoinPool.WorkQueue workQueue; // work-stealing mechanics | ||
|
||
/** | ||
* Creates a ForkJoinWorkerThread operating in the given pool. | ||
* | ||
* @param pool the pool this thread works in | ||
* @throws NullPointerException if pool is null | ||
*/ | ||
protected ForkJoinWorkerThread(ForkJoinPool pool) { | ||
// Use a placeholder until a useful name can be set in registerWorker | ||
super(NAME_PLACEHOLDER); | ||
this.pool = pool; | ||
this.workQueue = pool.registerWorker(this); | ||
} | ||
|
||
// note that this will never get called on Android | ||
/** | ||
* Version for InnocuousForkJoinWorkerThread. | ||
*/ | ||
ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup, | ||
AccessControlContext acc) { | ||
super(threadGroup, NAME_PLACEHOLDER); | ||
TLRandom.setInheritedAccessControlContext(this, acc); | ||
TLRandom.eraseThreadLocals(this); // clear before registering | ||
this.pool = pool; | ||
this.workQueue = pool.registerWorker(this); | ||
} | ||
|
||
/** | ||
* Returns the pool hosting this thread. | ||
* | ||
* @return the pool | ||
*/ | ||
public ForkJoinPool getPool() { | ||
return pool; | ||
} | ||
|
||
/** | ||
* Returns the unique index number of this thread in its pool. | ||
* The returned value ranges from zero to the maximum number of | ||
* threads (minus one) that may exist in the pool, and does not | ||
* change during the lifetime of the thread. This method may be | ||
* useful for applications that track status or collect results | ||
* per-worker-thread rather than per-task. | ||
* | ||
* @return the index number | ||
*/ | ||
public int getPoolIndex() { | ||
return workQueue.getPoolIndex(); | ||
} | ||
|
||
/** | ||
* Initializes internal state after construction but before | ||
* processing any tasks. If you override this method, you must | ||
* invoke {@code super.onStart()} at the beginning of the method. | ||
* Initialization requires care: Most fields must have legal | ||
* default values, to ensure that attempted accesses from other | ||
* threads work correctly even before this thread starts | ||
* processing tasks. | ||
*/ | ||
protected void onStart() { | ||
} | ||
|
||
/** | ||
* Performs cleanup associated with termination of this worker | ||
* thread. If you override this method, you must invoke | ||
* {@code super.onTermination} at the end of the overridden method. | ||
* | ||
* @param exception the exception causing this thread to abort due | ||
* to an unrecoverable error, or {@code null} if completed normally | ||
*/ | ||
protected void onTermination(Throwable exception) { | ||
} | ||
|
||
/** | ||
* This method is required to be public, but should never be | ||
* called explicitly. It performs the main run loop to execute | ||
* {@link ForkJoinTask}s. | ||
*/ | ||
public void run() { | ||
if (workQueue.array == null) { // only run once | ||
Throwable exception = null; | ||
try { | ||
onStart(); | ||
pool.runWorker(workQueue); | ||
} catch (Throwable ex) { | ||
exception = ex; | ||
} finally { | ||
try { | ||
onTermination(exception); | ||
} catch (Throwable ex) { | ||
if (exception == null) { | ||
exception = ex; | ||
} | ||
} finally { | ||
pool.deregisterWorker(this, exception); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Non-public hook method for InnocuousForkJoinWorkerThread. | ||
*/ | ||
void afterTopLevelExec() { | ||
} | ||
|
||
// note that this will never get called on Android! | ||
/** | ||
* A worker thread that has no permissions, is not a member of any | ||
* user-defined ThreadGroup, and erases all ThreadLocals after | ||
* running each top-level task. | ||
*/ | ||
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread { | ||
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */ | ||
private static final ThreadGroup innocuousThreadGroup = | ||
java.security.AccessController.doPrivileged( | ||
new java.security.PrivilegedAction<ThreadGroup>() { | ||
public ThreadGroup run() { | ||
ThreadGroup group = Thread.currentThread().getThreadGroup(); | ||
for (ThreadGroup p; (p = group.getParent()) != null; ) | ||
group = p; | ||
return new ThreadGroup(group, "InnocuousForkJoinWorkerThreadGroup"); | ||
}}); | ||
|
||
/** An AccessControlContext supporting no privileges */ | ||
private static final AccessControlContext INNOCUOUS_ACC = | ||
new AccessControlContext( | ||
new ProtectionDomain[] { | ||
new ProtectionDomain(null, null) | ||
}); | ||
|
||
InnocuousForkJoinWorkerThread(ForkJoinPool pool) { | ||
super(pool, innocuousThreadGroup, INNOCUOUS_ACC); | ||
} | ||
|
||
@Override // to erase ThreadLocals | ||
void afterTopLevelExec() { | ||
TLRandom.eraseThreadLocals(this); | ||
} | ||
|
||
@Override // to always report system loader | ||
public ClassLoader getContextClassLoader() { | ||
return ClassLoader.getSystemClassLoader(); | ||
} | ||
|
||
@Override // to silently fail | ||
public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) { } | ||
|
||
@Override // paranoically | ||
public void setContextClassLoader(ClassLoader cl) { | ||
throw new SecurityException("setContextClassLoader"); | ||
} | ||
} | ||
} |
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
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
Oops, something went wrong.