Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
0.1
Browse files Browse the repository at this point in the history
🦖
reg unreg for objects with sub fields
sync threads by event type
async test
qol consumer
more tests
fix sub compare
also remove proxy method on(Event)
readme
  • Loading branch information
nothub committed Feb 9, 2021
0 parents commit 1fb340f
Show file tree
Hide file tree
Showing 15 changed files with 501 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# https://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.{md, markdown}]
trim_trailing_whitespace = false

[*.{yml, yaml}]
indent_size = 2
15 changes: 15 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "daily"
time: "12:00"
timezone: "Europe/Berlin"
assignees:
- "nothub"
reviewers:
- "nothub"
commit-message:
prefix: "maven"
include: "scope"
34 changes: 34 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: 🦖

on:
push:
branches:
- 'master'
tags:
- '*'
pull_request:
branches:
- '*'

jobs:
tests:
runs-on: ubuntu-latest
steps:

- name: checkout
uses: actions/checkout@v2

- name: java
uses: actions/setup-java@v1
with:
java-version: 1.8

- name: cache
uses: actions/cache@v2
with:
path: ~/.m2
key: '${{ runner.os }}-m2-${{ hashFiles(''**/pom.xml'') }}'
restore-keys: '${{ runner.os }}-m2'

- name: test
run: mvn --batch-mode --show-version --errors --file pom.xml
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*
!*/

!/*.md
!/.editorconfig
!/.github/**
!/.gitignore
!/LICENSE
!/pom.xml
!/src/**
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
###### register event consumer

```java
class Example {
void example() {
Consumer<BooleanEvent> consumer = System.out::println;
Sub<BooleanEvent> sub = new Sub<>(BooleanEvent.class, consumer);
Bus bus = new Bus();
bus.reg(sub);
bus.pub(new BooleanEvent(true));
}
}
```

---

###### register consumer fields

```java
class Example {
boolean invoked = false;
Sub<BooleanEvent> sub = new Sub<>(BooleanEvent.class, e -> invoked = e.bool);

void example() {
Bus bus = new Bus();
bus.regFields(this);
bus.pub(new BooleanEvent(true));
}
}
```

---

###### listen to canceled events

```java
public class EventCanceledEvent extends Event {
Event e;

public EventCanceledEvent(Event e) {
this.e = e;
}
}
```
55 changes: 55 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cc.neckbeard</groupId>
<artifactId>TinyPubSub</artifactId>
<version>0.1</version>

<name>${project.artifactId}</name>
<packaging>jar</packaging>

<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<defaultGoal>clean test</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>20.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
71 changes: 71 additions & 0 deletions src/main/java/cc/neckbeard/tinypubsub/Bus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cc.neckbeard.tinypubsub;

import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Collectors;

public class Bus {

private final Map<Class<?>, Object> locks = new ConcurrentHashMap<>();
private final Map<Class<?>, ConcurrentSkipListSet<Sub>> subs = new ConcurrentHashMap<>();

private static Set<Sub> getSubs(Object obj) {
return Arrays
.stream(obj.getClass().getDeclaredFields())
.filter(field -> field.getType().equals(Sub.class))
.map(field -> {
boolean access = field.isAccessible();
field.setAccessible(true);
try {
return field.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
} finally {
field.setAccessible(access);
}
return null;
})
.filter(Objects::nonNull)
.map(o -> (Sub) o)
.collect(Collectors.toSet());
}

public void reg(@NotNull Sub sub) {
synchronized (locks.computeIfAbsent(sub.type, c -> new Object())) {
if (this.subs.computeIfAbsent(sub.type, c -> new ConcurrentSkipListSet<>()).contains(sub)) return;
this.subs.get(sub.type).add(sub);
}
}

public void regFields(@NotNull Object o) {
getSubs(o).forEach(this::reg);
}

public void unreg(@NotNull Sub sub) {
synchronized (locks.computeIfAbsent(sub.type, c -> new Object())) {
if (this.subs.get(sub.type) == null) return;
this.subs.get(sub.type).removeIf(s -> s.equals(sub));
}
}

public void unregFields(@NotNull Object o) {
getSubs(o).forEach(this::unreg);
}

public void pub(@NotNull Event e) {
synchronized (locks.computeIfAbsent(e.getClass(), c -> new Object())) {
this.subs.computeIfAbsent(e.getClass(), c -> new ConcurrentSkipListSet<>())
.forEach(sub -> {
if (e.isCancelled()) return;
sub.consumer.accept(e);
});
}
}

}
12 changes: 12 additions & 0 deletions src/main/java/cc/neckbeard/tinypubsub/Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cc.neckbeard.tinypubsub;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// TODO: @DarkiBoi

@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
int prio() default 0;
Class<? extends Event> type();
}
15 changes: 15 additions & 0 deletions src/main/java/cc/neckbeard/tinypubsub/Event.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cc.neckbeard.tinypubsub;

public abstract class Event {

private boolean cancelled;

public void cancel() {
this.cancelled = true;
}

public boolean isCancelled() {
return this.cancelled;
}

}
28 changes: 28 additions & 0 deletions src/main/java/cc/neckbeard/tinypubsub/Sub.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cc.neckbeard.tinypubsub;

import org.jetbrains.annotations.NotNull;

import java.util.function.Consumer;

public class Sub<E extends Event> implements Comparable<Sub<E>> {

public final int prio;
public final Class<E> type;
public final Consumer<E> consumer;

public Sub(int prio, Class<E> type, Consumer<E> consumer) {
this.prio = prio;
this.type = type;
this.consumer = consumer;
}

public Sub(Class<E> type, Consumer<E> consumer) {
this(0, type, consumer);
}

@Override
public int compareTo(@NotNull Sub sub) {
return sub.prio == prio ? sub.consumer.getClass().getCanonicalName().compareTo(consumer.getClass().getCanonicalName()) : Integer.compare(sub.prio, prio);
}

}
50 changes: 50 additions & 0 deletions src/test/java/cc/neckbeard/tinypubsub/tests/AsyncTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cc.neckbeard.tinypubsub.tests;

import cc.neckbeard.tinypubsub.Bus;
import cc.neckbeard.tinypubsub.Sub;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

import java.util.stream.IntStream;

@Execution(ExecutionMode.CONCURRENT)
class AsyncTests {

int runs = 0;

Sub<BooleanEvent> a = new Sub<>(0, BooleanEvent.class, e -> runs++);
Sub<BooleanEvent> b = new Sub<>(1, BooleanEvent.class, e -> runs++);
Sub<BooleanEvent> c = new Sub<>(2 , BooleanEvent.class, e -> runs++);

@Test
@DisplayName("unreg while pub")
void unreg() throws InterruptedException {

Bus bus = new Bus();
bus.regFields(this);

Thread t1 = new Thread(() -> IntStream
.range(0, 1000000)
.forEach(i -> bus.pub(new BooleanEvent(true))));

Thread t2 = new Thread(() -> IntStream
.range(0, 10000)
.forEach(i -> {
bus.unregFields(this);
bus.reg(a);
}));

t1.start();
t2.start();

t1.join();
t2.join();

Assertions.assertTrue(1000000 * 3 > runs);

}

}
13 changes: 13 additions & 0 deletions src/test/java/cc/neckbeard/tinypubsub/tests/BooleanEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cc.neckbeard.tinypubsub.tests;

import cc.neckbeard.tinypubsub.Event;

class BooleanEvent extends Event {

public boolean bool;

public BooleanEvent(boolean bool) {
this.bool = bool;
}

}
Loading

0 comments on commit 1fb340f

Please sign in to comment.