Skip to content

Commit 2f4e263

Browse files
authored
Merge pull request #105 from mvallim/release/1.0.8
Auto-created pull request into `master` from `release/1.0.8`
2 parents 46cfd0a + 2c78d30 commit 2f4e263

File tree

11 files changed

+109
-94
lines changed

11 files changed

+109
-94
lines changed

.github/workflows/cd-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
if: ${{ inputs.environment == 'release' }}
5252
name: Set output variables
5353
run: |
54-
RELEASE_VERSION=$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec | grep -e '^[^\[]' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
54+
RELEASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | grep -e '^[^\[]' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
5555
RELEASE_TAG=v${RELEASE_VERSION}
5656
5757
echo "release-version=$RELEASE_VERSION" >> $GITHUB_OUTPUT

.github/workflows/cd-integration.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
- id: environment
3333
name: Set output environment passed to the reusable workflow
3434
run: |
35-
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | cut -d '-' -f 1)
35+
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | grep -e '^[^\[]' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
3636
echo "version=$VERSION" >> $GITHUB_OUTPUT
3737
echo "target-branch=release/$VERSION" >> $GITHUB_OUTPUT
3838
@@ -55,4 +55,4 @@ jobs:
5555
type: Snapshot
5656
labels: automatic,snapshot
5757
source-branch: master
58-
target-branch: ${{ needs.variables.outputs.target-branch }}
58+
target-branch: ${{ needs.variables.outputs.target-branch }}

.github/workflows/ci.yml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,12 @@ jobs:
2020
outputs:
2121
type: ${{ steps.environment.outputs.type }}
2222
labels: ${{ steps.environment.outputs.labels }}
23-
target-branch: ${{ steps.environment.outputs.target-branch }}
2423
steps:
25-
- name: Checkout repository
26-
uses: actions/checkout@v4
27-
28-
- name: Set up JDK
29-
uses: actions/setup-java@v4
30-
with:
31-
java-version: 17
32-
distribution: "corretto"
33-
cache: "maven"
34-
3524
- id: environment
3625
name: Set output environment passed to the reusable workflow
3726
run: |
38-
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
39-
echo "version=$VERSION" >> $GITHUB_OUTPUT
4027
echo "type=$TYPE" >> $GITHUB_OUTPUT
4128
echo "labels=$LABELS" >> $GITHUB_OUTPUT
42-
echo "target-branch=release/$VERSION" >> $GITHUB_OUTPUT
4329
4430
ci:
4531
needs: variables

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,4 @@ logs/
9494
pom.xml.releaseBackup
9595
.attach_*
9696
release.properties
97+
**/pom.xml.versionsBackup

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ You can pull it from the central Maven repositories:
3838
<dependency>
3939
<groupId>com.github.mvallim</groupId>
4040
<artifactId>amazon-sns-java-messaging-lib-v1</artifactId>
41-
<version>1.0.7</version>
41+
<version>1.0.8</version>
4242
</dependency>
4343
```
4444

@@ -48,7 +48,7 @@ You can pull it from the central Maven repositories:
4848
<dependency>
4949
<groupId>com.github.mvallim</groupId>
5050
<artifactId>amazon-sns-java-messaging-lib-v2</artifactId>
51-
<version>1.0.7</version>
51+
<version>1.0.8</version>
5252
</dependency>
5353
```
5454

@@ -70,13 +70,13 @@ If you want to try a snapshot version, add the following repository:
7070
### For AWS SDK v1
7171

7272
```groovy
73-
implementation 'com.github.mvallim:amazon-sns-java-messaging-lib-v1:1.0.7'
73+
implementation 'com.github.mvallim:amazon-sns-java-messaging-lib-v1:1.0.8'
7474
```
7575

7676
### For AWS SDK v2
7777

7878
```groovy
79-
implementation 'com.github.mvallim:amazon-sns-java-messaging-lib-v2:1.0.7'
79+
implementation 'com.github.mvallim:amazon-sns-java-messaging-lib-v2:1.0.8'
8080
```
8181

8282
If you want to try a snapshot version, add the following repository:

amazon-sns-java-messaging-lib-template/pom.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
24

35
<modelVersion>4.0.0</modelVersion>
46

57
<parent>
68
<groupId>com.github.mvallim</groupId>
79
<artifactId>amazon-sns-java-messaging-lib</artifactId>
8-
<version>1.0.7-SNAPSHOT</version>
10+
<version>1.0.8-SNAPSHOT</version>
911
<relativePath>../pom.xml</relativePath>
1012
</parent>
1113

amazon-sns-java-messaging-lib-template/src/main/java/com/amazon/sns/messaging/lib/concurrent/RingBufferBlockingQueue.java

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,110 +17,110 @@
1717
package com.amazon.sns.messaging.lib.concurrent;
1818

1919
import java.util.AbstractQueue;
20-
import java.util.Arrays;
2120
import java.util.Collection;
2221
import java.util.Iterator;
2322
import java.util.concurrent.BlockingQueue;
2423
import java.util.concurrent.TimeUnit;
2524
import java.util.concurrent.atomic.AtomicInteger;
25+
import java.util.concurrent.atomic.AtomicLong;
26+
import java.util.concurrent.atomic.AtomicReferenceArray;
2627
import java.util.concurrent.locks.Condition;
2728
import java.util.concurrent.locks.ReentrantLock;
29+
import java.util.stream.IntStream;
2830

2931
import lombok.Getter;
3032
import lombok.Setter;
3133
import lombok.SneakyThrows;
3234

33-
@SuppressWarnings({ "java:S2274", "unchecked" })
35+
@SuppressWarnings({ "java:S2274" })
3436
public class RingBufferBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {
3537

3638
private static final int DEFAULT_CAPACITY = 2048;
3739

38-
private final Entry<E>[] buffer;
40+
private final AtomicReferenceArray<Entry<E>> buffer;
3941

4042
private final int capacity;
4143

42-
private final AtomicInteger writeSequence = new AtomicInteger(-1);
44+
private final AtomicLong writeSequence = new AtomicLong(-1);
4345

44-
private final AtomicInteger readSequence = new AtomicInteger(0);
46+
private final AtomicLong readSequence = new AtomicLong(0);
47+
48+
private final AtomicInteger size = new AtomicInteger(0);
4549

4650
private final ReentrantLock reentrantLock;
4751

48-
private final Condition notEmpty;
52+
private final Condition waitingConsumer;
4953

50-
private final Condition notFull;
54+
private final Condition waitingProducer;
5155

5256
public RingBufferBlockingQueue(final int capacity) {
5357
this.capacity = capacity;
54-
this.buffer = new Entry[capacity];
55-
Arrays.setAll(buffer, p -> new Entry<>());
58+
buffer = new AtomicReferenceArray<>(capacity);
5659
reentrantLock = new ReentrantLock(true);
57-
notEmpty = reentrantLock.newCondition();
58-
notFull = reentrantLock.newCondition();
60+
waitingConsumer = reentrantLock.newCondition();
61+
waitingProducer = reentrantLock.newCondition();
62+
IntStream.range(0, capacity).forEach(idx -> buffer.set(idx, new Entry<>()));
5963
}
6064

6165
public RingBufferBlockingQueue() {
6266
this(DEFAULT_CAPACITY);
6367
}
6468

65-
private void enqueue(final E element) throws InterruptedException {
66-
while (isFull()) {
67-
notFull.await();
68-
}
69-
70-
final int nextWriteSeq = writeSequence.get() + 1;
71-
buffer[wrap(nextWriteSeq)].setValue(element);
72-
writeSequence.incrementAndGet();
73-
notEmpty.signal();
69+
private long avoidSequenceOverflow(final long sequence) {
70+
return (sequence < Long.MAX_VALUE ? sequence : wrap(sequence));
7471
}
7572

76-
private E dequeue() throws InterruptedException {
77-
while (isEmpty()) {
78-
notEmpty.await();
79-
}
80-
81-
final E nextValue = buffer[wrap(readSequence.get())].getValue();
82-
readSequence.incrementAndGet();
83-
notFull.signal();
84-
return nextValue;
85-
}
86-
87-
private int wrap(final int sequence) {
88-
return sequence % capacity;
73+
private int wrap(final long sequence) {
74+
return Math.toIntExact(sequence % capacity);
8975
}
9076

9177
@Override
9278
public int size() {
93-
return (writeSequence.get() - readSequence.get()) + 1;
79+
return size.get();
9480
}
9581

9682
@Override
9783
public boolean isEmpty() {
98-
return writeSequence.get() < readSequence.get();
84+
return size.get() == 0;
9985
}
10086

10187
public boolean isFull() {
102-
return size() >= capacity;
88+
return size.get() >= capacity;
10389
}
10490

105-
public int writeSequence() {
91+
public long writeSequence() {
10692
return writeSequence.get();
10793
}
10894

109-
public int readSequence() {
95+
public long readSequence() {
11096
return readSequence.get();
11197
}
11298

11399
@Override
114100
public E peek() {
115-
return isEmpty() ? null : buffer[wrap(readSequence.get())].getValue();
101+
return isEmpty() ? null : buffer.get(wrap(readSequence.get())).getValue();
116102
}
117103

118104
@Override
119105
@SneakyThrows
120106
public void put(final E element) {
121107
try {
122108
reentrantLock.lock();
123-
enqueue(element);
109+
110+
while (isFull()) {
111+
waitingProducer.await();
112+
}
113+
114+
final long prevWriteSeq = writeSequence.get();
115+
final long nextWriteSeq = avoidSequenceOverflow(prevWriteSeq) + 1;
116+
117+
buffer.get(wrap(nextWriteSeq)).setValue(element);
118+
119+
writeSequence.compareAndSet(prevWriteSeq, nextWriteSeq);
120+
121+
size.incrementAndGet();
122+
123+
waitingConsumer.signal();
124124
} finally {
125125
reentrantLock.unlock();
126126
}
@@ -131,7 +131,25 @@ public void put(final E element) {
131131
public E take() {
132132
try {
133133
reentrantLock.lock();
134-
return dequeue();
134+
135+
while (isEmpty()) {
136+
waitingConsumer.await();
137+
}
138+
139+
final long prevReadSeq = readSequence.get();
140+
final long nextReadSeq = avoidSequenceOverflow(prevReadSeq) + 1;
141+
142+
final E nextValue = buffer.get(wrap(prevReadSeq)).getValue();
143+
144+
buffer.get(wrap(prevReadSeq)).setValue(null);
145+
146+
readSequence.compareAndSet(prevReadSeq, nextReadSeq);
147+
148+
size.decrementAndGet();
149+
150+
waitingProducer.signal();
151+
152+
return nextValue;
135153
} finally {
136154
reentrantLock.unlock();
137155
}

amazon-sns-java-messaging-lib-template/src/test/java/com/amazon/sns/messaging/lib/concurrent/RingBufferBlockingQueueTest.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import static org.hamcrest.MatcherAssert.assertThat;
2222
import static org.hamcrest.Matchers.hasSize;
2323
import static org.junit.jupiter.api.Assertions.assertThrows;
24-
import static org.mockito.Mockito.spy;
2524

2625
import java.util.Collections;
2726
import java.util.LinkedList;
@@ -42,17 +41,18 @@ class RingBufferBlockingQueueTest {
4241

4342
@Test
4443
void testSuccess() {
45-
final ExecutorService producer = Executors.newSingleThreadExecutor();
44+
final ExecutorService producer = Executors.newSingleThreadScheduledExecutor();
4645

4746
final ScheduledExecutorService consumer = Executors.newSingleThreadScheduledExecutor();
4847

4948
final List<RequestEntry<Integer>> requestEntriesOut = new LinkedList<>();
5049

51-
final RingBufferBlockingQueue<RequestEntry<Integer>> ringBlockingQueue = new RingBufferBlockingQueue<>(5120);
50+
final RingBufferBlockingQueue<RequestEntry<Integer>> ringBlockingQueue = new RingBufferBlockingQueue<>();
5251

5352
producer.submit(() -> {
5453
IntStream.range(0, 100_000).forEach(value -> {
5554
ringBlockingQueue.put(RequestEntry.<Integer>builder().withValue(value).build());
55+
System.err.println("write: " + ringBlockingQueue.writeSequence());
5656
});
5757
});
5858

@@ -61,19 +61,23 @@ void testSuccess() {
6161
final List<RequestEntry<Integer>> requestEntries = new LinkedList<>();
6262

6363
while ((requestEntries.size() < 10) && Objects.nonNull(ringBlockingQueue.peek())) {
64-
requestEntries.add(ringBlockingQueue.take());
64+
final RequestEntry<Integer> take = ringBlockingQueue.take();
65+
System.err.println("read: " + ringBlockingQueue.readSequence());
66+
requestEntries.add(take);
6567
}
6668

6769
requestEntriesOut.addAll(requestEntries);
6870
}
6971
}, 0, 100L, TimeUnit.MILLISECONDS);
7072

71-
await().atMost(1, TimeUnit.MINUTES).until(() -> ringBlockingQueue.writeSequence() == 99_999);
72-
producer.shutdownNow();
73+
await().pollInterval(5, TimeUnit.SECONDS).pollDelay(200, TimeUnit.MILLISECONDS).until(() -> {
74+
return (ringBlockingQueue.writeSequence() == 99_999) && (ringBlockingQueue.readSequence() == 100_000);
75+
});
7376

74-
await().atMost(1, TimeUnit.MINUTES).until(() -> ringBlockingQueue.readSequence() == 100_000);
75-
consumer.shutdownNow();
77+
producer.shutdown();
78+
consumer.shutdown();
7679

80+
assertThat(ringBlockingQueue.size(), is(0));
7781
assertThat(ringBlockingQueue.isEmpty(), is(true));
7882

7983
assertThat(requestEntriesOut, hasSize(100_000));
@@ -85,8 +89,8 @@ void testSuccess() {
8589
}
8690

8791
@Test
88-
void testSuccessWhenIsEmpty() throws InterruptedException {
89-
final RingBufferBlockingQueue<RequestEntry<Integer>> ringBlockingQueue = spy(new RingBufferBlockingQueue<>());
92+
void testSuccessWhenIsEmpty() {
93+
final RingBufferBlockingQueue<RequestEntry<Integer>> ringBlockingQueue = new RingBufferBlockingQueue<>();
9094

9195
final ExecutorService producer = Executors.newSingleThreadExecutor();
9296

@@ -97,7 +101,7 @@ void testSuccessWhenIsEmpty() throws InterruptedException {
97101
assertThat(ringBlockingQueue.take().getValue(), is(1));
98102
});
99103

100-
Thread.sleep(2000);
104+
await().pollDelay(2, TimeUnit.SECONDS).until(() -> true);
101105

102106
producer.submit(() -> {
103107
ringBlockingQueue.put(RequestEntry.<Integer>builder().withValue(0).build());
@@ -114,8 +118,8 @@ void testSuccessWhenIsEmpty() throws InterruptedException {
114118
}
115119

116120
@Test
117-
void testSuccessWhenIsFull() throws InterruptedException {
118-
final RingBufferBlockingQueue<RequestEntry<Integer>> ringBlockingQueue = spy(new RingBufferBlockingQueue<>(1));
121+
void testSuccessWhenIsFull() {
122+
final RingBufferBlockingQueue<RequestEntry<Integer>> ringBlockingQueue = new RingBufferBlockingQueue<>(1);
119123

120124
final ExecutorService producer = Executors.newSingleThreadExecutor();
121125

@@ -126,7 +130,7 @@ void testSuccessWhenIsFull() throws InterruptedException {
126130
ringBlockingQueue.put(RequestEntry.<Integer>builder().withValue(1).build());
127131
});
128132

129-
Thread.sleep(2000);
133+
await().pollDelay(2, TimeUnit.SECONDS).until(() -> true);
130134

131135
consumer.submit(() -> {
132136
assertThat(ringBlockingQueue.take().getValue(), is(0));

0 commit comments

Comments
 (0)