ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jdk7/java/util/concurrent/Exchanger.java
Revision: 1.4
Committed: Thu Feb 26 06:53:34 2015 UTC (9 years, 3 months ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +0 -4 lines
Log Message:
delete unused imports

File Contents

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