ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/Exchanger.java
Revision: 1.66
Committed: Wed Dec 31 07:54:13 2014 UTC (9 years, 5 months ago) by jsr166
Branch: MAIN
Changes since 1.65: +1 -0 lines
Log Message:
standardize import statement order

File Contents

# Content
1 /*
2 * Written by Doug Lea, Bill Scherer, and Michael Scott with
3 * assistance from members of JCP JSR-166 Expert Group and released to
4 * the public domain, as explained at
5 * http://creativecommons.org/publicdomain/zero/1.0/
6 */
7
8 package java.util.concurrent;
9
10 import java.util.concurrent.atomic.AtomicInteger;
11 import java.util.concurrent.atomic.AtomicReference;
12 import java.util.concurrent.locks.LockSupport;
13
14 /**
15 * A synchronization point at which threads can pair and swap elements
16 * within pairs. Each thread presents some object on entry to the
17 * {@link #exchange exchange} method, matches with a partner thread,
18 * and receives its partner's object on return. An Exchanger may be
19 * viewed as a bidirectional form of a {@link SynchronousQueue}.
20 * Exchangers may be useful in applications such as genetic algorithms
21 * and pipeline designs.
22 *
23 * <p><b>Sample Usage:</b>
24 * Here are the highlights of a class that uses an {@code Exchanger}
25 * to swap buffers between threads so that the thread filling the
26 * buffer gets a freshly emptied one when it needs it, handing off the
27 * filled one to the thread emptying the buffer.
28 * <pre> {@code
29 * class FillAndEmpty {
30 * Exchanger<DataBuffer> exchanger = new Exchanger<>();
31 * DataBuffer initialEmptyBuffer = ... a made-up type
32 * DataBuffer initialFullBuffer = ...
33 *
34 * class FillingLoop implements Runnable {
35 * public void run() {
36 * DataBuffer currentBuffer = initialEmptyBuffer;
37 * try {
38 * while (currentBuffer != null) {
39 * addToBuffer(currentBuffer);
40 * if (currentBuffer.isFull())
41 * currentBuffer = exchanger.exchange(currentBuffer);
42 * }
43 * } catch (InterruptedException ex) { ... handle ... }
44 * }
45 * }
46 *
47 * class EmptyingLoop implements Runnable {
48 * public void run() {
49 * DataBuffer currentBuffer = initialFullBuffer;
50 * try {
51 * while (currentBuffer != null) {
52 * takeFromBuffer(currentBuffer);
53 * if (currentBuffer.isEmpty())
54 * currentBuffer = exchanger.exchange(currentBuffer);
55 * }
56 * } catch (InterruptedException ex) { ... handle ...}
57 * }
58 * }
59 *
60 * void start() {
61 * new Thread(new FillingLoop()).start();
62 * new Thread(new EmptyingLoop()).start();
63 * }
64 * }}</pre>
65 *
66 * <p>Memory consistency effects: For each pair of threads that
67 * successfully exchange objects via an {@code Exchanger}, actions
68 * prior to the {@code exchange()} in each thread
69 * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
70 * those subsequent to a return from the corresponding {@code exchange()}
71 * in the other thread.
72 *
73 * @since 1.5
74 * @author Doug Lea and Bill Scherer and Michael Scott
75 * @param <V> The type of objects that may be exchanged
76 */
77 public class Exchanger<V> {
78
79 /*
80 * Overview: The core algorithm is, for an exchange "slot",
81 * and a participant (caller) with an item:
82 *
83 * for (;;) {
84 * if (slot is empty) { // offer
85 * place item in a Node;
86 * if (can CAS slot from empty to node) {
87 * wait for release;
88 * return matching item in node;
89 * }
90 * }
91 * else if (can CAS slot from node to empty) { // release
92 * get the item in node;
93 * set matching item in node;
94 * release waiting thread;
95 * }
96 * // else retry on CAS failure
97 * }
98 *
99 * This is among the simplest forms of a "dual data structure" --
100 * see Scott and Scherer's DISC 04 paper and
101 * http://www.cs.rochester.edu/research/synchronization/pseudocode/duals.html
102 *
103 * This works great in principle. But in practice, like many
104 * algorithms centered on atomic updates to a single location, it
105 * scales horribly when there are more than a few participants
106 * using the same Exchanger. So the implementation instead uses a
107 * form of elimination arena, that spreads out this contention by
108 * arranging that some threads typically use different slots,
109 * while still ensuring that eventually, any two parties will be
110 * able to exchange items. That is, we cannot completely partition
111 * across threads, but instead give threads arena indices that
112 * will on average grow under contention and shrink under lack of
113 * contention. We approach this by defining the Nodes that we need
114 * anyway as ThreadLocals, and include in them per-thread index
115 * and related bookkeeping state. (We can safely reuse per-thread
116 * nodes rather than creating them fresh each time because slots
117 * alternate between pointing to a node vs null, so cannot
118 * encounter ABA problems. However, we do need some care in
119 * resetting them between uses.)
120 *
121 * Implementing an effective arena requires allocating a bunch of
122 * space, so we only do so upon detecting contention (except on
123 * uniprocessors, where they wouldn't help, so aren't used).
124 * Otherwise, exchanges use the single-slot slotExchange method.
125 * On contention, not only must the slots be in different
126 * locations, but the locations must not encounter memory
127 * contention due to being on the same cache line (or more
128 * generally, the same coherence unit). Because, as of this
129 * writing, there is no way to determine cacheline size, we define
130 * a value that is enough for common platforms. Additionally,
131 * extra care elsewhere is taken to avoid other false/unintended
132 * sharing and to enhance locality, including adding padding (via
133 * sun.misc.Contended) to Nodes, embedding "bound" as an Exchanger
134 * field, and reworking some park/unpark mechanics compared to
135 * LockSupport versions.
136 *
137 * The arena starts out with only one used slot. We expand the
138 * effective arena size by tracking collisions; i.e., failed CASes
139 * while trying to exchange. By nature of the above algorithm, the
140 * only kinds of collision that reliably indicate contention are
141 * when two attempted releases collide -- one of two attempted
142 * offers can legitimately fail to CAS without indicating
143 * contention by more than one other thread. (Note: it is possible
144 * but not worthwhile to more precisely detect contention by
145 * reading slot values after CAS failures.) When a thread has
146 * collided at each slot within the current arena bound, it tries
147 * to expand the arena size by one. We track collisions within
148 * bounds by using a version (sequence) number on the "bound"
149 * field, and conservatively reset collision counts when a
150 * participant notices that bound has been updated (in either
151 * direction).
152 *
153 * The effective arena size is reduced (when there is more than
154 * one slot) by giving up on waiting after a while and trying to
155 * decrement the arena size on expiration. The value of "a while"
156 * is an empirical matter. We implement by piggybacking on the
157 * use of spin->yield->block that is essential for reasonable
158 * waiting performance anyway -- in a busy exchanger, offers are
159 * usually almost immediately released, in which case context
160 * switching on multiprocessors is extremely slow/wasteful. Arena
161 * waits just omit the blocking part, and instead cancel. The spin
162 * count is empirically chosen to be a value that avoids blocking
163 * 99% of the time under maximum sustained exchange rates on a
164 * range of test machines. Spins and yields entail some limited
165 * randomness (using a cheap xorshift) to avoid regular patterns
166 * that can induce unproductive grow/shrink cycles. (Using a
167 * pseudorandom also helps regularize spin cycle duration by
168 * making branches unpredictable.) Also, during an offer, a
169 * waiter can "know" that it will be released when its slot has
170 * changed, but cannot yet proceed until match is set. In the
171 * mean time it cannot cancel the offer, so instead spins/yields.
172 * Note: It is possible to avoid this secondary check by changing
173 * the linearization point to be a CAS of the match field (as done
174 * in one case in the Scott & Scherer DISC paper), which also
175 * increases asynchrony a bit, at the expense of poorer collision
176 * detection and inability to always reuse per-thread nodes. So
177 * the current scheme is typically a better tradeoff.
178 *
179 * On collisions, indices traverse the arena cyclically in reverse
180 * order, restarting at the maximum index (which will tend to be
181 * sparsest) when bounds change. (On expirations, indices instead
182 * are halved until reaching 0.) It is possible (and has been
183 * tried) to use randomized, prime-value-stepped, or double-hash
184 * style traversal instead of simple cyclic traversal to reduce
185 * bunching. But empirically, whatever benefits these may have
186 * don't overcome their added overhead: We are managing operations
187 * that occur very quickly unless there is sustained contention,
188 * so simpler/faster control policies work better than more
189 * accurate but slower ones.
190 *
191 * Because we use expiration for arena size control, we cannot
192 * throw TimeoutExceptions in the timed version of the public
193 * exchange method until the arena size has shrunken to zero (or
194 * the arena isn't enabled). This may delay response to timeout
195 * but is still within spec.
196 *
197 * Essentially all of the implementation is in methods
198 * slotExchange and arenaExchange. These have similar overall
199 * structure, but differ in too many details to combine. The
200 * slotExchange method uses the single Exchanger field "slot"
201 * rather than arena array elements. However, it still needs
202 * minimal collision detection to trigger arena construction.
203 * (The messiest part is making sure interrupt status and
204 * InterruptedExceptions come out right during transitions when
205 * both methods may be called. This is done by using null return
206 * as a sentinel to recheck interrupt status.)
207 *
208 * As is too common in this sort of code, methods are monolithic
209 * because most of the logic relies on reads of fields that are
210 * maintained as local variables so can't be nicely factored --
211 * mainly, here, bulky spin->yield->block/cancel code), and
212 * heavily dependent on intrinsics (Unsafe) to use inlined
213 * embedded CAS and related memory access operations (that tend
214 * not to be as readily inlined by dynamic compilers when they are
215 * hidden behind other methods that would more nicely name and
216 * encapsulate the intended effects). This includes the use of
217 * putOrderedX to clear fields of the per-thread Nodes between
218 * uses. Note that field Node.item is not declared as volatile
219 * even though it is read by releasing threads, because they only
220 * do so after CAS operations that must precede access, and all
221 * uses by the owning thread are otherwise acceptably ordered by
222 * other operations. (Because the actual points of atomicity are
223 * slot CASes, it would also be legal for the write to Node.match
224 * in a release to be weaker than a full volatile write. However,
225 * this is not done because it could allow further postponement of
226 * the write, delaying progress.)
227 */
228
229 /**
230 * The byte distance (as a shift value) between any two used slots
231 * in the arena. 1 << ASHIFT should be at least cacheline size.
232 */
233 private static final int ASHIFT = 7;
234
235 /**
236 * The maximum supported arena index. The maximum allocatable
237 * arena size is MMASK + 1. Must be a power of two minus one, less
238 * than (1<<(31-ASHIFT)). The cap of 255 (0xff) more than suffices
239 * for the expected scaling limits of the main algorithms.
240 */
241 private static final int MMASK = 0xff;
242
243 /**
244 * Unit for sequence/version bits of bound field. Each successful
245 * change to the bound also adds SEQ.
246 */
247 private static final int SEQ = MMASK + 1;
248
249 /** The number of CPUs, for sizing and spin control */
250 private static final int NCPU = Runtime.getRuntime().availableProcessors();
251
252 /**
253 * The maximum slot index of the arena: The number of slots that
254 * can in principle hold all threads without contention, or at
255 * most the maximum indexable value.
256 */
257 static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1;
258
259 /**
260 * The bound for spins while waiting for a match. The actual
261 * number of iterations will on average be about twice this value
262 * due to randomization. Note: Spinning is disabled when NCPU==1.
263 */
264 private static final int SPINS = 1 << 10;
265
266 /**
267 * Value representing null arguments/returns from public
268 * methods. Needed because the API originally didn't disallow null
269 * arguments, which it should have.
270 */
271 private static final Object NULL_ITEM = new Object();
272
273 /**
274 * Sentinel value returned by internal exchange methods upon
275 * timeout, to avoid need for separate timed versions of these
276 * methods.
277 */
278 private static final Object TIMED_OUT = new Object();
279
280 /**
281 * Nodes hold partially exchanged data, plus other per-thread
282 * bookkeeping. Padded via @sun.misc.Contended to reduce memory
283 * contention.
284 */
285 @sun.misc.Contended static final class Node {
286 int index; // Arena index
287 int bound; // Last recorded value of Exchanger.bound
288 int collides; // Number of CAS failures at current bound
289 int hash; // Pseudo-random for spins
290 Object item; // This thread's current item
291 volatile Object match; // Item provided by releasing thread
292 volatile Thread parked; // Set to this thread when parked, else null
293 }
294
295 /** The corresponding thread local class */
296 static final class Participant extends ThreadLocal<Node> {
297 public Node initialValue() { return new Node(); }
298 }
299
300 /**
301 * Per-thread state
302 */
303 private final Participant participant;
304
305 /**
306 * Elimination array; null until enabled (within slotExchange).
307 * Element accesses use emulation of volatile gets and CAS.
308 */
309 private volatile Node[] arena;
310
311 /**
312 * Slot used until contention detected.
313 */
314 private volatile Node slot;
315
316 /**
317 * The index of the largest valid arena position, OR'ed with SEQ
318 * number in high bits, incremented on each update. The initial
319 * update from 0 to SEQ is used to ensure that the arena array is
320 * constructed only once.
321 */
322 private volatile int bound;
323
324 /**
325 * Exchange function when arenas enabled. See above for explanation.
326 *
327 * @param item the (non-null) item to exchange
328 * @param timed true if the wait is timed
329 * @param ns if timed, the maximum wait time, else 0L
330 * @return the other thread's item; or null if interrupted; or
331 * TIMED_OUT if timed and timed out
332 */
333 private final Object arenaExchange(Object item, boolean timed, long ns) {
334 Node[] a = arena;
335 Node p = participant.get();
336 for (int i = p.index;;) { // access slot at i
337 int b, m, c; long j; // j is raw array offset
338 Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
339 if (q != null && U.compareAndSwapObject(a, j, q, null)) {
340 Object v = q.item; // release
341 q.match = item;
342 Thread w = q.parked;
343 if (w != null)
344 U.unpark(w);
345 return v;
346 }
347 else if (i <= (m = (b = bound) & MMASK) && q == null) {
348 p.item = item; // offer
349 if (U.compareAndSwapObject(a, j, null, p)) {
350 long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
351 Thread t = Thread.currentThread(); // wait
352 for (int h = p.hash, spins = SPINS;;) {
353 Object v = p.match;
354 if (v != null) {
355 U.putOrderedObject(p, MATCH, null);
356 p.item = null; // clear for next use
357 p.hash = h;
358 return v;
359 }
360 else if (spins > 0) {
361 h ^= h << 1; h ^= h >>> 3; h ^= h << 10; // xorshift
362 if (h == 0) // initialize hash
363 h = SPINS | (int)t.getId();
364 else if (h < 0 && // approx 50% true
365 (--spins & ((SPINS >>> 1) - 1)) == 0)
366 Thread.yield(); // two yields per wait
367 }
368 else if (U.getObjectVolatile(a, j) != p)
369 spins = SPINS; // releaser hasn't set match yet
370 else if (!t.isInterrupted() && m == 0 &&
371 (!timed ||
372 (ns = end - System.nanoTime()) > 0L)) {
373 U.putObject(t, BLOCKER, this); // emulate LockSupport
374 p.parked = t; // minimize window
375 if (U.getObjectVolatile(a, j) == p)
376 U.park(false, ns);
377 p.parked = null;
378 U.putObject(t, BLOCKER, null);
379 }
380 else if (U.getObjectVolatile(a, j) == p &&
381 U.compareAndSwapObject(a, j, p, null)) {
382 if (m != 0) // try to shrink
383 U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
384 p.item = null;
385 p.hash = h;
386 i = p.index >>>= 1; // descend
387 if (Thread.interrupted())
388 return null;
389 if (timed && m == 0 && ns <= 0L)
390 return TIMED_OUT;
391 break; // expired; restart
392 }
393 }
394 }
395 else
396 p.item = null; // clear offer
397 }
398 else {
399 if (p.bound != b) { // stale; reset
400 p.bound = b;
401 p.collides = 0;
402 i = (i != m || m == 0) ? m : m - 1;
403 }
404 else if ((c = p.collides) < m || m == FULL ||
405 !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
406 p.collides = c + 1;
407 i = (i == 0) ? m : i - 1; // cyclically traverse
408 }
409 else
410 i = m + 1; // grow
411 p.index = i;
412 }
413 }
414 }
415
416 /**
417 * Exchange function used until arenas enabled. See above for explanation.
418 *
419 * @param item the item to exchange
420 * @param timed true if the wait is timed
421 * @param ns if timed, the maximum wait time, else 0L
422 * @return the other thread's item; or null if either the arena
423 * was enabled or the thread was interrupted before completion; or
424 * TIMED_OUT if timed and timed out
425 */
426 private final Object slotExchange(Object item, boolean timed, long ns) {
427 Node p = participant.get();
428 Thread t = Thread.currentThread();
429 if (t.isInterrupted()) // preserve interrupt status so caller can recheck
430 return null;
431
432 for (Node q;;) {
433 if ((q = slot) != null) {
434 if (U.compareAndSwapObject(this, SLOT, q, null)) {
435 Object v = q.item;
436 q.match = item;
437 Thread w = q.parked;
438 if (w != null)
439 U.unpark(w);
440 return v;
441 }
442 // create arena on contention, but continue until slot null
443 if (NCPU > 1 && bound == 0 &&
444 U.compareAndSwapInt(this, BOUND, 0, SEQ))
445 arena = new Node[(FULL + 2) << ASHIFT];
446 }
447 else if (arena != null)
448 return null; // caller must reroute to arenaExchange
449 else {
450 p.item = item;
451 if (U.compareAndSwapObject(this, SLOT, null, p))
452 break;
453 p.item = null;
454 }
455 }
456
457 // await release
458 int h = p.hash;
459 long end = timed ? System.nanoTime() + ns : 0L;
460 int spins = (NCPU > 1) ? SPINS : 1;
461 Object v;
462 while ((v = p.match) == null) {
463 if (spins > 0) {
464 h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
465 if (h == 0)
466 h = SPINS | (int)t.getId();
467 else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
468 Thread.yield();
469 }
470 else if (slot != p)
471 spins = SPINS;
472 else if (!t.isInterrupted() && arena == null &&
473 (!timed || (ns = end - System.nanoTime()) > 0L)) {
474 U.putObject(t, BLOCKER, this);
475 p.parked = t;
476 if (slot == p)
477 U.park(false, ns);
478 p.parked = null;
479 U.putObject(t, BLOCKER, null);
480 }
481 else if (U.compareAndSwapObject(this, SLOT, p, null)) {
482 v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
483 break;
484 }
485 }
486 U.putOrderedObject(p, MATCH, null);
487 p.item = null;
488 p.hash = h;
489 return v;
490 }
491
492 /**
493 * Creates a new Exchanger.
494 */
495 public Exchanger() {
496 participant = new Participant();
497 }
498
499 /**
500 * Waits for another thread to arrive at this exchange point (unless
501 * the current thread is {@linkplain Thread#interrupt interrupted}),
502 * and then transfers the given object to it, receiving its object
503 * in return.
504 *
505 * <p>If another thread is already waiting at the exchange point then
506 * it is resumed for thread scheduling purposes and receives the object
507 * passed in by the current thread. The current thread returns immediately,
508 * receiving the object passed to the exchange by that other thread.
509 *
510 * <p>If no other thread is already waiting at the exchange then the
511 * current thread is disabled for thread scheduling purposes and lies
512 * dormant until one of two things happens:
513 * <ul>
514 * <li>Some other thread enters the exchange; or
515 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
516 * the current thread.
517 * </ul>
518 * <p>If the current thread:
519 * <ul>
520 * <li>has its interrupted status set on entry to this method; or
521 * <li>is {@linkplain Thread#interrupt interrupted} while waiting
522 * for the exchange,
523 * </ul>
524 * then {@link InterruptedException} is thrown and the current thread's
525 * interrupted status is cleared.
526 *
527 * @param x the object to exchange
528 * @return the object provided by the other thread
529 * @throws InterruptedException if the current thread was
530 * interrupted while waiting
531 */
532 @SuppressWarnings("unchecked")
533 public V exchange(V x) throws InterruptedException {
534 Object v;
535 Object item = (x == null) ? NULL_ITEM : x; // translate null args
536 if ((arena != null ||
537 (v = slotExchange(item, false, 0L)) == null) &&
538 ((Thread.interrupted() || // disambiguates null return
539 (v = arenaExchange(item, false, 0L)) == null)))
540 throw new InterruptedException();
541 return (v == NULL_ITEM) ? null : (V)v;
542 }
543
544 /**
545 * Waits for another thread to arrive at this exchange point (unless
546 * the current thread is {@linkplain Thread#interrupt interrupted} or
547 * the specified waiting time elapses), and then transfers the given
548 * object to it, receiving its object in return.
549 *
550 * <p>If another thread is already waiting at the exchange point then
551 * it is resumed for thread scheduling purposes and receives the object
552 * passed in by the current thread. The current thread returns immediately,
553 * receiving the object passed to the exchange by that other thread.
554 *
555 * <p>If no other thread is already waiting at the exchange then the
556 * current thread is disabled for thread scheduling purposes and lies
557 * dormant until one of three things happens:
558 * <ul>
559 * <li>Some other thread enters the exchange; or
560 * <li>Some other thread {@linkplain Thread#interrupt interrupts}
561 * the current thread; or
562 * <li>The specified waiting time elapses.
563 * </ul>
564 * <p>If the current thread:
565 * <ul>
566 * <li>has its interrupted status set on entry to this method; or
567 * <li>is {@linkplain Thread#interrupt interrupted} while waiting
568 * for the exchange,
569 * </ul>
570 * then {@link InterruptedException} is thrown and the current thread's
571 * interrupted status is cleared.
572 *
573 * <p>If the specified waiting time elapses then {@link
574 * TimeoutException} is thrown. If the time is less than or equal
575 * to zero, the method will not wait at all.
576 *
577 * @param x the object to exchange
578 * @param timeout the maximum time to wait
579 * @param unit the time unit of the {@code timeout} argument
580 * @return the object provided by the other thread
581 * @throws InterruptedException if the current thread was
582 * interrupted while waiting
583 * @throws TimeoutException if the specified waiting time elapses
584 * before another thread enters the exchange
585 */
586 @SuppressWarnings("unchecked")
587 public V exchange(V x, long timeout, TimeUnit unit)
588 throws InterruptedException, TimeoutException {
589 Object v;
590 Object item = (x == null) ? NULL_ITEM : x;
591 long ns = unit.toNanos(timeout);
592 if ((arena != null ||
593 (v = slotExchange(item, true, ns)) == null) &&
594 ((Thread.interrupted() ||
595 (v = arenaExchange(item, true, ns)) == null)))
596 throw new InterruptedException();
597 if (v == TIMED_OUT)
598 throw new TimeoutException();
599 return (v == NULL_ITEM) ? null : (V)v;
600 }
601
602 // Unsafe mechanics
603 private static final sun.misc.Unsafe U;
604 private static final long BOUND;
605 private static final long SLOT;
606 private static final long MATCH;
607 private static final long BLOCKER;
608 private static final int ABASE;
609 static {
610 int s;
611 try {
612 U = sun.misc.Unsafe.getUnsafe();
613 Class<?> ek = Exchanger.class;
614 Class<?> nk = Node.class;
615 Class<?> ak = Node[].class;
616 Class<?> tk = Thread.class;
617 BOUND = U.objectFieldOffset
618 (ek.getDeclaredField("bound"));
619 SLOT = U.objectFieldOffset
620 (ek.getDeclaredField("slot"));
621 MATCH = U.objectFieldOffset
622 (nk.getDeclaredField("match"));
623 BLOCKER = U.objectFieldOffset
624 (tk.getDeclaredField("parkBlocker"));
625 s = U.arrayIndexScale(ak);
626 // ABASE absorbs padding in front of element 0
627 ABASE = U.arrayBaseOffset(ak) + (1 << ASHIFT);
628
629 } catch (Exception e) {
630 throw new Error(e);
631 }
632 if ((s & (s-1)) != 0 || s > (1 << ASHIFT))
633 throw new Error("Unsupported array scale");
634 }
635
636 }