Slide 1

Slide 1 text

Spéléo Reactor @simonbasle Dans les profondeurs de la Programmation Réactive

Slide 2

Slide 2 text

Au Programme

Slide 3

Slide 3 text

● introduction / reactive streams ● map ● la fusion ● concatMap ● la “drain loop”

Slide 4

Slide 4 text

Introduction

Slide 5

Slide 5 text

Programmation Réactive

Slide 6

Slide 6 text

“Composer des séquences asynchrones, basées sur les événements avec des opérateurs non-bloquants”

Slide 7

Slide 7 text

Reactive Streams

Slide 8

Slide 8 text

Reactive Streams standard d’interopérabilité JDK 9 Flow Publisher Subscriber Subscription

Slide 9

Slide 9 text

Standard d’interopérabilité

Slide 10

Slide 10 text

Aussi dans JDK 9 (Flow)

Slide 11

Slide 11 text

Publisher produit la donnée

Slide 12

Slide 12 text

Subscriber consomme la donnée

Slide 13

Slide 13 text

Publisher Subscriber pousse des éléments produit consomme feedback

Slide 14

Slide 14 text

subscribe(Subscriber) Publisher Subscriber

Slide 15

Slide 15 text

onNext(T) onError(Throwable) onComplete() Publisher Subscriber

Slide 16

Slide 16 text

Publisher Subscriber pousse des éléments produit consomme feedback “0 à N onNext suivi d’au plus 1 (onComplete ou onError)”

Slide 17

Slide 17 text

onNext(T) onError(Throwable) onComplete() Publisher Subscriber

Slide 18

Slide 18 text

+ onSubscribe(Subscription) onNext(T) onError(Throwable) onComplete() Publisher Subscriber

Slide 19

Slide 19 text

Subscription requête et annulation

Slide 20

Slide 20 text

Subscriber au Publisher: “je suis disposé à traiter N éléments, envoie les-moi dès que possible”

Slide 21

Slide 21 text

C’est la Backpressure

Slide 22

Slide 22 text

à cet effet le Publisher fourni une Subscription au Subscriber

Slide 23

Slide 23 text

grâce à elle, le Subscriber peut notifier de sa capacité de traitement via Subscription#request(long)

Slide 24

Slide 24 text

Publisher Subscriber pousse des éléments produit consomme request(n) backpressure

Slide 25

Slide 25 text

Modèle dit “Asynchronous Push-Pull”

Slide 26

Slide 26 text

Modèle dit “Asynchronous Push-Pull”

Slide 27

Slide 27 text

Modèle dit “Asynchronous Push-Pull” Le Subscriber demande N éléments au Publisher (notamment à la souscription)

Slide 28

Slide 28 text

Modèle dit “Asynchronous Push-Pull”

Slide 29

Slide 29 text

Modèle dit “Asynchronous Push-Pull” Le Publisher envoie au max le nombre demandé, en asynchrone (au fil de l’eau)

Slide 30

Slide 30 text

Diagramme à billes (“marble diagram”)

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

onNext

Slide 33

Slide 33 text

onComplete

Slide 34

Slide 34 text

la séquence

Slide 35

Slide 35 text

direction du “temps”

Slide 36

Slide 36 text

plus de délai entre ces deux onNext

Slide 37

Slide 37 text

une séquence qui tombe en erreur (onError)

Slide 38

Slide 38 text

Contraintes intéressantes de Concurrence

Slide 39

Slide 39 text

les onNext, ainsi que les événements terminaux, sont en série

Slide 40

Slide 40 text

les onNext, ainsi que les événements terminaux, sont en série ie. pas d’appel parallèle à onNext pendant un onNext

Slide 41

Slide 41 text

request & cancel peuvent se produire en parallèle (de onNext et d’eux même) req(2) req(7) req(1)

Slide 42

Slide 42 text

can I have an API though? Publisher Subscriber

Slide 43

Slide 43 text

Publisher Subscriber

Slide 44

Slide 44 text

Reactor

Slide 45

Slide 45 text

Reactor et son vocabulaire d’opérateurs Reactive Streams de bout en bout Flux Mono Riche API d’opérateurs

Slide 46

Slide 46 text

Tous les types sont des Publisher

Slide 47

Slide 47 text

Flux pour 0-N éléments

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Mono pour 0-1 élément

Slide 50

Slide 50 text

valué vide en erreur

Slide 51

Slide 51 text

Large vocabulaire d’Opérateurs

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Autres Concepts Importants

Slide 54

Slide 54 text

Autres Concepts Importants souscription vers le haut, données vers le bas rien ne se passe avant `subscribe()` enchaîner les opérateurs

Slide 55

Slide 55 text

souscription de bas en haut données de haut en bas

Slide 56

Slide 56 text

Flux/Mono generator operator operator operator

Slide 57

Slide 57 text

Flux/Mono generator Subscriber operator operator operator

Slide 58

Slide 58 text

Flux/Mono generator Subscriber operator operator operator état propre à chaque Subscription Sub Sub Sub

Slide 59

Slide 59 text

Flux/Mono generator Subscriber operator operator operator le flux de données commence Sub Sub Sub

Slide 60

Slide 60 text

les opérateurs sont à la fois des Publisher et des Subscriber

Slide 61

Slide 61 text

rien ne se passe tant qu’on n’a pas subscribe( )

Slide 62

Slide 62 text

(dans la plupart des cas) la source des données ne commence à émettre que lors de la souscription

Slide 63

Slide 63 text

crée une chaine de subscribers dédiés propage la 1ère requête, qui “démarre” la source

Slide 64

Slide 64 text

enchaîner les opérateurs

Slide 65

Slide 65 text

chaque opérateur retourne une nouvelle instance qui décore l’opérateur précédent.

Slide 66

Slide 66 text

il faut donc enchaîner les appels (le style “fluent API”)

Slide 67

Slide 67 text

Première descente: map

Slide 68

Slide 68 text

L’opérateur map que fait l’opérateur ? à quoi ressemble le code ?

Slide 69

Slide 69 text

que fait l’opérateur?

Slide 70

Slide 70 text

Flux map(Function f)

Slide 71

Slide 71 text

Flux map(Function f)

Slide 72

Slide 72 text

Flux map(Function f)

Slide 73

Slide 73 text

Flux map(Function f)

Slide 74

Slide 74 text

Flux map(Function f)

Slide 75

Slide 75 text

Flux map(Function f)

Slide 76

Slide 76 text

T R map( )

Slide 77

Slide 77 text

T R map( )

Slide 78

Slide 78 text

à quoi ressemble le code?

Slide 79

Slide 79 text

le Publisher l’opérateur, instancié par la méthode Flux#map

Slide 80

Slide 80 text

final class FluxMap extends FluxOperator { final Function super T, ? extends R> mapper; FluxMap(Flux extends T> source, Function super T, ? extends R> mapper) { super(source); this.mapper = Objects.requireNonNull(mapper, "mapper"); } @Override @SuppressWarnings("unchecked") public void subscribe(CoreSubscriber super R> actual) { source.subscribe(new MapSubscriber<>(actual, mapper)); } }

Slide 81

Slide 81 text

final class FluxMap extends FluxOperator { final Function super T, ? extends R> mapper; FluxMap(Flux extends T> source, Function super T, ? extends R> mapper) { super(source); this.mapper = Objects.requireNonNull(mapper, "mapper"); } @Override @SuppressWarnings("unchecked") public void subscribe(CoreSubscriber super R> actual) { source.subscribe(new MapSubscriber<>(actual, mapper)); } }

Slide 82

Slide 82 text

le MapSubscriber

Slide 83

Slide 83 text

static final class MapSubscriber implements InnerOperator { final CoreSubscriber super R> actual; final Function super T, ? extends R> mapper; boolean done; Subscription s; MapSubscriber(CoreSubscriber super R> actual, Function super T, ? extends R> mapper) { this.actual = actual; this.mapper = mapper; } ...

Slide 84

Slide 84 text

static final class MapSubscriber implements InnerOperator { final CoreSubscriber super R> actual; final Function super T, ? extends R> mapper; boolean done; Subscription s; MapSubscriber(CoreSubscriber super R> actual, Function super T, ? extends R> mapper) { this.actual = actual; this.mapper = mapper; }

Slide 85

Slide 85 text

final CoreSubscriber super R> actual; Subscription s; ... @Override public void onSubscribe(Subscription s) { if (Operators.validate(this.s, s)) { this.s = s; actual.onSubscribe(this); } }

Slide 86

Slide 86 text

final CoreSubscriber super R> actual; boolean done; ... @Override public void onComplete() { if (done) { return; } done = true; actual.onComplete(); }

Slide 87

Slide 87 text

final CoreSubscriber super R> actual; boolean done; ... @Override public void onError(Throwable t) { if (done) { return; } done = true; actual.onError(t); }

Slide 88

Slide 88 text

static final class MapSubscriber implements InnerOperator { ... Subscription s; ... @Override public void request(long n) { s.request(n); } @Override public void cancel() { s.cancel(); } }

Slide 89

Slide 89 text

son onNext le coeur de métier de l’opérateur

Slide 90

Slide 90 text

@Override public void onNext(T t) { if (done) { return; } R v; try { v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value."); } catch (Throwable e) { onError(e); return; } actual.onNext(v); }

Slide 91

Slide 91 text

@Override public void onNext(T t) { if (done) { return; } R v; try { v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value."); } catch (Throwable e) { onError(e); return; } actual.onNext(v); }

Slide 92

Slide 92 text

pause 15min

Slide 93

Slide 93 text

La Fusion map peut être fusionné comment la fusion est-elle implémentée ? quel impact sur la chaîne d’opérateurs ?

Slide 94

Slide 94 text

map peut être Fusionné

Slide 95

Slide 95 text

le subscriber est aussi une Queue réutilisée par chaque opérateur fusionné en dessous

Slide 96

Slide 96 text

remplace des queues internes peut remonter jusqu’à la source (eg. Flux.fromIterable)

Slide 97

Slide 97 text

opérateur source opérateur 2 opérateur 3 sans fusion

Slide 98

Slide 98 text

opérateur source opérateur 2 opérateur 3 avec fusion

Slide 99

Slide 99 text

Impact sur le code interface Fuseable

Slide 100

Slide 100 text

final class FluxMapFuseable extends FluxOperator implements Fuseable { final Function super T, ? extends R> mapper; FluxMapFuseable(Flux extends T> source, Function super T, ? extends R> mapper) { super(source); this.mapper = Objects.requireNonNull(mapper, "mapper"); } @Override public void subscribe(CoreSubscriber super R> actual) { source.subscribe(new MapFuseableSubscriber<>(actual, mapper)); }

Slide 101

Slide 101 text

final class FluxMapFuseable extends FluxOperator implements Fuseable { final Function super T, ? extends R> mapper; FluxMapFuseable(Flux extends T> source, Function super T, ? extends R> mapper) { super(source); this.mapper = Objects.requireNonNull(mapper, "mapper"); } @Override public void subscribe(CoreSubscriber super R> actual) { source.subscribe(new MapFuseableSubscriber<>(actual, mapper)); }

Slide 102

Slide 102 text

static final class MapFuseableSubscriber implements InnerOperator, QueueSubscription { final CoreSubscriber super R> actual; final Function super T, ? extends R> mapper; boolean done; QueueSubscription s; int sourceMode; MapFuseableSubscriber(CoreSubscriber super R> actual, Function super T, ? extends R> mapper) { this.actual = actual; this.mapper = mapper; }

Slide 103

Slide 103 text

static final class MapFuseableSubscriber implements InnerOperator, QueueSubscription { final CoreSubscriber super R> actual; final Function super T, ? extends R> mapper; boolean done; QueueSubscription s; int sourceMode; MapFuseableSubscriber(CoreSubscriber super R> actual, Function super T, ? extends R> mapper) { this.actual = actual; this.mapper = mapper; }

Slide 104

Slide 104 text

négociation de la fusion

Slide 105

Slide 105 text

@Override public int requestFusion(int requestedMode) { int m; if ((requestedMode & Fuseable.THREAD_BARRIER) != 0) { return Fuseable.NONE; } else { m = s.requestFusion(requestedMode); } sourceMode = m; return m; }

Slide 106

Slide 106 text

@Override public int requestFusion(int requestedMode) { int m; if ((requestedMode & Fuseable.THREAD_BARRIER) != 0) { return Fuseable.NONE; } else { m = s.requestFusion(requestedMode); } sourceMode = m; return m; }

Slide 107

Slide 107 text

null comme signal de fusion poll comme mécanisme

Slide 108

Slide 108 text

@Override public void onNext(T t) { if (sourceMode == ASYNC) { actual.onNext(null); } else { if (done) { return; } R v; try { v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value."); } catch (Throwable e) { onError(e); return;

Slide 109

Slide 109 text

@Override public void onNext(T t) { if (sourceMode == ASYNC) { actual.onNext(null); } else { if (done) { return; } R v; try { v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value."); } catch (Throwable e) { onError(e); return;

Slide 110

Slide 110 text

@Override @Nullable public R poll() { T v = s.poll(); if (v != null) { //can throw exceptions return Objects.requireNonNull(mapper.apply(v)); } return null; }

Slide 111

Slide 111 text

@Override @Nullable public R poll() { T v = s.poll(); if (v != null) { //can throw exceptions return Objects.requireNonNull(mapper.apply(v)); } return null; }

Slide 112

Slide 112 text

@Override public boolean isEmpty() { return s.isEmpty(); } @Override public void clear() { s.clear(); } @Override public int size() { return s.size(); }

Slide 113

Slide 113 text

Deuxième descente: concatMap

Slide 114

Slide 114 text

l’opérateur concatMap que fait-il ? opérateurs 1-to-N opérateur interne et Subscriber interne

Slide 115

Slide 115 text

que fait l’opérateur?

Slide 116

Slide 116 text

Flux concatMap( Function> f)

Slide 117

Slide 117 text

Flux concatMap( Function> f)

Slide 118

Slide 118 text

Flux concatMap( Function> f)

Slide 119

Slide 119 text

ça ressemble à une fonction qui pourrait être passée à map

Slide 120

Slide 120 text

essayons...

Slide 121

Slide 121 text

T map( )

Slide 122

Slide 122 text

T map( )

Slide 123

Slide 123 text

T map( )

Slide 124

Slide 124 text

T map( ) ceux-ci sont “inertes” Flux>

Slide 125

Slide 125 text

map n’as aucune connaissance particulière sur le type

Slide 126

Slide 126 text

ici est en fait Publisher seulement map ne sait pas qu’il doit y souscrire...

Slide 127

Slide 127 text

T concatMap( )

Slide 128

Slide 128 text

concatMap( )

Slide 129

Slide 129 text

concatMap( )

Slide 130

Slide 130 text

concatMap( ) sous-flux souscrits et propagés Flux

Slide 131

Slide 131 text

concatMap sait quoi faire d’un Publisher il y souscrit, et propage ses éléments

Slide 132

Slide 132 text

concatMap concatène: pas de souscription au sous-Publisher suivant tant que le précédent n’as pas terminé

Slide 133

Slide 133 text

opérateurs 1-to-N concatMap vs flatMap

Slide 134

Slide 134 text

concatMap( )

Slide 135

Slide 135 text

flatMap( )

Slide 136

Slide 136 text

à quoi ressemble le code?

Slide 137

Slide 137 text

opérateur de base Publisher classique

Slide 138

Slide 138 text

final class FluxConcatMap extends FluxOperator { final Function super T, ? extends Publisher extends R>> mapper; final Supplier extends Queue> queueSupplier; final int prefetch; FluxConcatMap(Flux extends T> source, Function super T, ? extends Publisher extends R>> mapper, Supplier extends Queue> queueSupplier, int prefetch) { super(source); if (prefetch <= 0) throw new IllegalArgumentException("prefetch <= 0"); this.mapper = Objects.requireNonNull(mapper, "mapper"); this.queueSupplier = Objects.requireNonNull(queueSupplier); this.prefetch = prefetch; } @Override public void subscribe(CoreSubscriber super R> actual) { source.subscribe(new ConcatMapImmediate<>(s, mapper, queueSupplier, prefetch)); }

Slide 139

Slide 139 text

final class FluxConcatMap extends FluxOperator { final Function super T, ? extends Publisher extends R>> mapper; final Supplier extends Queue> queueSupplier; final int prefetch; FluxConcatMap(Flux extends T> source, Function super T, ? extends Publisher extends R>> mapper, Supplier extends Queue> queueSupplier, int prefetch) { super(source); if (prefetch <= 0) throw new IllegalArgumentException("prefetch <= 0"); this.mapper = Objects.requireNonNull(mapper, "mapper"); this.queueSupplier = Objects.requireNonNull(queueSupplier); this.prefetch = prefetch; } @Override public void subscribe(CoreSubscriber super R> actual) { source.subscribe(new ConcatMapImmediate<>(s, mapper, queueSupplier, prefetch)); }

Slide 140

Slide 140 text

ConcatMapImmediate Subscriber principal avec un peu plus d’état, coordonne les sous-Publisher

Slide 141

Slide 141 text

static final class ConcatMapImmediate { final CoreSubscriber super R> actual; final Function super T, ? extends Publisher extends R>> mapper; final Supplier extends Queue> queueSupplier; final int prefetch; final int limit; Subscription s; int consumed; volatile Queue queue; volatile boolean done; volatile boolean cancelled; volatile boolean active; volatile Throwable error; volatile int wip; volatile int guard; int sourceMode;

Slide 142

Slide 142 text

static final class ConcatMapImmediate { final CoreSubscriber super R> actual; final Function super T, ? extends Publisher extends R>> mapper; final Supplier extends Queue> queueSupplier; final int prefetch; final int limit; Subscription s; int consumed; volatile Queue queue; volatile boolean done; volatile boolean cancelled; volatile boolean active; volatile Throwable error; volatile int wip; volatile int guard; int sourceMode;

Slide 143

Slide 143 text

static final AtomicReferenceFieldUpdater ERROR = AtomicReferenceFieldUpdater.newUpdater(ConcatMapImmediate.class, Throwable.class, "error"); static final AtomicIntegerFieldUpdater WIP = AtomicIntegerFieldUpdater.newUpdater(ConcatMapImmediate.class,"wip"); static final AtomicIntegerFieldUpdater GUARD = AtomicIntegerFieldUpdater.newUpdater(ConcatMapImmediate.class, "guard"); final ConcatMapInner inner;

Slide 144

Slide 144 text

@Override public void onSubscribe(Subscription s) { if (Operators.validate(this.s, s)) { this.s = s; if (s instanceof Fuseable.QueueSubscription) { if (m == Fuseable.SYNC) { ... } else if (m == Fuseable.ASYNC) { ... } else { queue = queueSupplier.get(); } } else { queue = queueSupplier.get(); } actual.onSubscribe(this); s.request(Operators.unboundedOrPrefetch(prefetch)); } }

Slide 145

Slide 145 text

... if (s instanceof Fuseable.QueueSubscription) { Fuseable.QueueSubscription f = (Fuseable.QueueSubscription) s; int m = f.requestFusion(Fuseable.ANY); if (m == Fuseable.SYNC) { sourceMode = Fuseable.SYNC; queue = f; done = true; actual.onSubscribe(this); drain(); return; } else if (m == Fuseable.ASYNC) { sourceMode = Fuseable.ASYNC; queue = f; } else { queue = queueSupplier.get(); } } ...

Slide 146

Slide 146 text

... if (s instanceof Fuseable.QueueSubscription) { Fuseable.QueueSubscription f = (Fuseable.QueueSubscription) s; int m = f.requestFusion(Fuseable.ANY); if (m == Fuseable.SYNC) { sourceMode = Fuseable.SYNC; queue = f; done = true; actual.onSubscribe(this); drain(); return; } else if (m == Fuseable.ASYNC) { sourceMode = Fuseable.ASYNC; queue = f; } else { queue = queueSupplier.get(); } } ... drain( )? on va y revenir...

Slide 147

Slide 147 text

@Override public void onComplete() { done = true; drain(); } @Override public void request(long n) { inner.request(n); } @Override public void cancel() { if (!cancelled) { cancelled = true; inner.cancel(); s.cancel(); } }

Slide 148

Slide 148 text

@Override public void onError(Throwable t) { if (Exceptions.addThrowable(ERROR, this, t)) { inner.cancel(); if (GUARD.getAndIncrement(this) == 0) { t = Exceptions.terminate(ERROR, this); if (t != TERMINATED) { actual.onError(t); } } } else { Operators.onErrorDropped(t, actual.currentContext()); } }

Slide 149

Slide 149 text

@Override public void onError(Throwable t) { if (Exceptions.addThrowable(ERROR, this, t)) { inner.cancel(); if (GUARD.getAndIncrement(this) == 0) { t = Exceptions.terminate(ERROR, this); if (t != TERMINATED) { actual.onError(t); } } } else { Operators.onErrorDropped(t, actual.currentContext()); } }

Slide 150

Slide 150 text

@Override public void onNext(T t) { if (sourceMode == Fuseable.ASYNC) { drain(); } else if (!queue.offer(t)) { onError(Exceptions.BACKPRESSURE_ERROR_QUEUE_FULL); } else { drain(); } }

Slide 151

Slide 151 text

@Override public void onNext(T t) { if (sourceMode == Fuseable.ASYNC) { drain(); } else if (!queue.offer(t)) { onError(Exceptions.BACKPRESSURE_ERROR_QUEUE_FULL); } else { drain(); } } on va y revenir je vous dis...

Slide 152

Slide 152 text

pour l’instant, précisons juste que dans le drain( ) on va attacher le inner au Publisher créé par la fonction

Slide 153

Slide 153 text

@Override public void onNext(T t) { if (sourceMode == Fuseable.ASYNC) { drain(); } else if (!queue.offer(t)) { onError(Exceptions.BACKPRESSURE_ERROR_QUEUE_FULL); } else { drain(); } }

Slide 154

Slide 154 text

le inner appelle par la suite son parent via l’API suivante

Slide 155

Slide 155 text

@Override public void innerNext(R value) { if (guard == 0 && GUARD.compareAndSet(this, 0, 1)) { actual.onNext(value); if (GUARD.compareAndSet(this, 1, 0)) { return; } Throwable e = Exceptions.terminate(ERROR, this); if (e != TERMINATED) { actual.onError(e); } } }

Slide 156

Slide 156 text

@Override public void innerComplete() { active = false; drain(); }

Slide 157

Slide 157 text

@Override public void innerError(Throwable e) { if (Exceptions.addThrowable(ERROR, this, e)) { s.cancel(); if (GUARD.getAndIncrement(this) == 0) { e = Exceptions.terminate(ERROR, this); if (e != TERMINATED) { actual.onError(e); } } } else { Operators.onErrorDropped(e, actual.currentContext()); } }

Slide 158

Slide 158 text

ConcatMapInner récupère les signaux des sous-Publisher s’assure que la backpressure est respectée

Slide 159

Slide 159 text

static final class ConcatMapInner extends Operators.MultiSubscriptionSubscriber { final FluxConcatMapImmediate, R> parent; long produced; ConcatMapInner(FluxConcatMapSupport, R> parent) { super(Operators.emptySubscriber()); this.parent = parent; } @Override public void onNext(R t) { produced++; parent.innerNext(t); } ...

Slide 160

Slide 160 text

static final class ConcatMapInner extends Operators.MultiSubscriptionSubscriber { final FluxConcatMapImmediate, R> parent; long produced; ConcatMapInner(FluxConcatMapSupport, R> parent) { super(Operators.emptySubscriber()); this.parent = parent; } @Override public void onNext(R t) { produced++; parent.innerNext(t); } ...

Slide 161

Slide 161 text

static final class ConcatMapInner extends Operators.MultiSubscriptionSubscriber { ... @Override public void onError(Throwable t) { long p = produced; if (p != 0L) { produced = 0L; produced(p); } parent.innerError(t); } ... }

Slide 162

Slide 162 text

static final class ConcatMapInner extends Operators.MultiSubscriptionSubscriber { ... @Override public void onComplete() { long p = produced; if (p != 0L) { produced = 0L; produced(p); } parent.innerComplete(); } } }

Slide 163

Slide 163 text

au plus profond: la drain loop

Slide 164

Slide 164 text

Au plus profond: la `drain loop` gérer les race conditions work stealing

Slide 165

Slide 165 text

void drain() { if (WIP.getAndIncrement(this) == 0) { for (; ; ) { if (cancelled) return; if (!active) { ... } if (WIP.decrementAndGet(this) == 0) { break; } } } } }

Slide 166

Slide 166 text

void drain() { if (WIP.getAndIncrement(this) == 0) { for (; ; ) { if (cancelled) return; if (!active) { ... } if (WIP.decrementAndGet(this) == 0) { break; } } } } }

Slide 167

Slide 167 text

if (cancelled) return; if (!active) { boolean d = done; T v; try { v = queue.poll(); } catch (Throwable e) { actual.onError(e); return; } boolean empty = v == null; if (d && empty) { actual.onComplete(); return; } ...

Slide 168

Slide 168 text

... if (!empty) { Publisher extends R> p; try { p = Objects.requireNonNull(mapper.apply(v)); } catch (Throwable e) { actual.onError(e); return; } if (sourceMode != Fuseable.SYNC) { ... } active = true; p.subscribe(inner); } } if (WIP.decrementAndGet(this) == 0) { break; }

Slide 169

Slide 169 text

... if (sourceMode != Fuseable.SYNC) { int c = consumed + 1; if (c == limit) { consumed = 0; s.request(c); } else { consumed = c; } } active = true; p.subscribe(inner); } } if (WIP.decrementAndGet(this) == 0) { break; }

Slide 170

Slide 170 text

work stealing

Slide 171

Slide 171 text

void drain() { if (WIP.getAndIncrement(this) == 0) { for (; ; ) { if (cancelled) return; if (!active) { ... } if (WIP.decrementAndGet(this) == 0) { break; } } } } }

Slide 172

Slide 172 text

thread 1 thread 2 thread 3 sans work stealing onNext(3) empile 3 1, 2, 3

Slide 173

Slide 173 text

thread 1 thread 2 thread 3 sans work stealing onNext(3) empile 3 request( 1 ) émet 1 2, 3

Slide 174

Slide 174 text

request( 2 ) thread 1 thread 2 thread 3 sans work stealing émet 2, 3 onNext(3) empile 3 request( 1 ) émet 1

Slide 175

Slide 175 text

thread 1 thread 2 thread 3 sans work stealing onNext(3) empile 3 bloqué draine bloqué draine

Slide 176

Slide 176 text

thread 1 thread 2 thread 3 avec work stealing onNext(3) empile 3 1, 2, 3

Slide 177

Slide 177 text

thread 1 thread 2 thread 3 avec work stealing onNext(3) empile 3 request( 1 ) émet 1 2, 3

Slide 178

Slide 178 text

request( 2 ) thread 1 thread 2 thread 3 avec work stealing onNext(3) empile 3 request( 1 ) émet 1 émet 2, 3

Slide 179

Slide 179 text

request( 2 ) thread 1 thread 2 thread 3 avec work stealing onNext(3) empile 3 request( 1 ) émet 1 émet 2, 3 met juste la requête à jour et quitte la boucle WIP cette drainLoop a “gagné”

Slide 180

Slide 180 text

Conclusion

Slide 181

Slide 181 text

Questions?

Slide 182

Slide 182 text

Merci !