Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
55ed5f9d
Commit
55ed5f9d
authored
Mar 20, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add concurrency control to event listeners
parent
1b152b28
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
75 additions
and
19 deletions
+75
-19
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
.../net.mamoe.mirai/event/internal/InternalEventListeners.kt
+22
-8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt
...src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt
+41
-9
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt
...kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt
+12
-2
No files found.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
View file @
55ed5f9d
...
@@ -34,22 +34,33 @@ fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L
...
@@ -34,22 +34,33 @@ fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L
@Suppress
(
"FunctionName"
)
@Suppress
(
"FunctionName"
)
internal
fun
<
E
:
Event
>
CoroutineScope
.
Handler
(
internal
fun
<
E
:
Event
>
CoroutineScope
.
Handler
(
coroutineContext
:
CoroutineContext
,
coroutineContext
:
CoroutineContext
,
concurrencyKind
:
Listener
.
ConcurrencyKind
,
handler
:
suspend
(
E
)
->
ListeningStatus
handler
:
suspend
(
E
)
->
ListeningStatus
):
Handler
<
E
>
{
):
Handler
<
E
>
{
@OptIn
(
ExperimentalCoroutinesApi
::
class
)
// don't remove
@OptIn
(
ExperimentalCoroutinesApi
::
class
)
// don't remove
val
context
=
this
.
newCoroutineContext
(
coroutineContext
)
val
context
=
this
.
newCoroutineContext
(
coroutineContext
)
return
Handler
(
context
[
Job
],
context
,
handler
)
return
Handler
(
context
[
Job
],
context
,
handler
,
concurrencyKind
)
}
}
private
inline
fun
inline
(
block
:
()
->
Unit
)
=
block
()
/**
/**
* 事件处理器.
* 事件处理器.
*/
*/
@PublishedApi
@PublishedApi
internal
class
Handler
<
in
E
:
Event
>
internal
class
Handler
<
in
E
:
Event
>
@PublishedApi
internal
constructor
(
parentJob
:
Job
?,
private
val
subscriberContext
:
CoroutineContext
,
@JvmField
val
handler
:
suspend
(
E
)
->
ListeningStatus
)
:
@PublishedApi
internal
constructor
(
parentJob
:
Job
?,
private
val
subscriberContext
:
CoroutineContext
,
@JvmField
val
handler
:
suspend
(
E
)
->
ListeningStatus
,
override
val
concurrencyKind
:
Listener
.
ConcurrencyKind
)
:
Listener
<
E
>,
CompletableJob
by
Job
(
parentJob
)
{
Listener
<
E
>,
CompletableJob
by
Job
(
parentJob
)
{
@MiraiInternalAPI
val
lock
:
Mutex
?
=
when
(
concurrencyKind
)
{
Listener
.
ConcurrencyKind
.
CONCURRENT
->
null
Listener
.
ConcurrencyKind
.
LOCKED
->
Mutex
()
}
@OptIn
(
MiraiDebugAPI
::
class
)
@OptIn
(
MiraiDebugAPI
::
class
)
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
{
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
{
if
(
isCompleted
||
isCancelled
)
return
ListeningStatus
.
STOPPED
if
(
isCompleted
||
isCancelled
)
return
ListeningStatus
.
STOPPED
...
@@ -60,7 +71,7 @@ internal class Handler<in E : Event>
...
@@ -60,7 +71,7 @@ internal class Handler<in E : Event>
}
catch
(
e
:
Throwable
)
{
}
catch
(
e
:
Throwable
)
{
subscriberContext
[
CoroutineExceptionHandler
]
?.
handleException
(
subscriberContext
,
e
)
subscriberContext
[
CoroutineExceptionHandler
]
?.
handleException
(
subscriberContext
,
e
)
?:
coroutineContext
[
CoroutineExceptionHandler
]
?.
handleException
(
subscriberContext
,
e
)
?:
coroutineContext
[
CoroutineExceptionHandler
]
?.
handleException
(
subscriberContext
,
e
)
?:
inline
{
?:
kotlin
.
run
{
@Suppress
(
"DEPRECATION"
)
@Suppress
(
"DEPRECATION"
)
MiraiLogger
.
warning
(
MiraiLogger
.
warning
(
"""Event processing: An exception occurred but no CoroutineExceptionHandler found,
"""Event processing: An exception occurred but no CoroutineExceptionHandler found,
...
@@ -75,9 +86,6 @@ internal class Handler<in E : Event>
...
@@ -75,9 +86,6 @@ internal class Handler<in E : Event>
ListeningStatus
.
LISTENING
ListeningStatus
.
LISTENING
}
}
}
}
@MiraiInternalAPI
override
val
lock
:
Mutex
=
Mutex
()
}
}
/**
/**
...
@@ -161,7 +169,13 @@ private fun <E : Event> CoroutineScope.callAndRemoveIfRequired(event: E, listene
...
@@ -161,7 +169,13 @@ private fun <E : Event> CoroutineScope.callAndRemoveIfRequired(event: E, listene
listeners
.
forEachNode
{
node
->
listeners
.
forEachNode
{
node
->
launch
{
launch
{
val
listener
=
node
.
nodeValue
val
listener
=
node
.
nodeValue
listener
.
lock
.
withLock
{
if
(
listener
.
concurrencyKind
==
Listener
.
ConcurrencyKind
.
LOCKED
)
{
(
listener
as
Handler
).
lock
!!
.
withLock
{
if
(!
node
.
isRemoved
()
&&
listener
.
onEvent
(
event
)
==
ListeningStatus
.
STOPPED
)
{
listeners
.
remove
(
listener
)
// atomic remove
}
}
}
else
{
if
(!
node
.
isRemoved
()
&&
listener
.
onEvent
(
event
)
==
ListeningStatus
.
STOPPED
)
{
if
(!
node
.
isRemoved
()
&&
listener
.
onEvent
(
event
)
==
ListeningStatus
.
STOPPED
)
{
listeners
.
remove
(
listener
)
// atomic remove
listeners
.
remove
(
listener
)
// atomic remove
}
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt
View file @
55ed5f9d
...
@@ -52,11 +52,23 @@ enum class ListeningStatus {
...
@@ -52,11 +52,23 @@ enum class ListeningStatus {
* 取消监听: [complete]
* 取消监听: [complete]
*/
*/
interface
Listener
<
in
E
:
Event
>
:
CompletableJob
{
interface
Listener
<
in
E
:
Event
>
:
CompletableJob
{
enum
class
ConcurrencyKind
{
/**
* 并发地同时处理多个事件, 但无法保证 [onEvent] 返回 [ListeningStatus.STOPPED] 后立即停止事件监听.
*/
CONCURRENT
,
/**
* 使用 [Mutex] 保证同一时刻只处理一个事件.
*/
LOCKED
}
/**
/**
*
[onEvent] 的锁
*
并发类型
*/
*/
@MiraiInternalAPI
val
concurrencyKind
:
ConcurrencyKind
val
lock
:
Mutex
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
}
}
...
@@ -121,9 +133,10 @@ interface Listener<in E : Event> : CompletableJob {
...
@@ -121,9 +133,10 @@ interface Listener<in E : Event> : CompletableJob {
@OptIn
(
MiraiInternalAPI
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
)
inline
fun
<
reified
E
:
Event
>
CoroutineScope
.
subscribe
(
inline
fun
<
reified
E
:
Event
>
CoroutineScope
.
subscribe
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
concurrency
:
Listener
.
ConcurrencyKind
=
Listener
.
ConcurrencyKind
.
LOCKED
,
noinline
handler
:
suspend
E
.(
E
)
->
ListeningStatus
noinline
handler
:
suspend
E
.(
E
)
->
ListeningStatus
):
Listener
<
E
>
=
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
)
{
it
.
handler
(
it
);
})
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
,
concurrency
)
{
it
.
handler
(
it
);
})
/**
/**
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
...
@@ -139,12 +152,17 @@ inline fun <reified E : Event> CoroutineScope.subscribe(
...
@@ -139,12 +152,17 @@ inline fun <reified E : Event> CoroutineScope.subscribe(
@OptIn
(
MiraiInternalAPI
::
class
,
ExperimentalContracts
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
,
ExperimentalContracts
::
class
)
inline
fun
<
reified
E
:
Event
>
CoroutineScope
.
subscribeAlways
(
inline
fun
<
reified
E
:
Event
>
CoroutineScope
.
subscribeAlways
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
concurrency
:
Listener
.
ConcurrencyKind
=
Listener
.
ConcurrencyKind
.
LOCKED
,
noinline
listener
:
suspend
E
.(
E
)
->
Unit
noinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
{
):
Listener
<
E
>
{
contract
{
contract
{
callsInPlace
(
listener
,
InvocationKind
.
UNKNOWN
)
callsInPlace
(
listener
,
InvocationKind
.
UNKNOWN
)
}
}
return
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
)
{
it
.
listener
(
it
);
ListeningStatus
.
LISTENING
})
return
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
,
concurrency
)
{
it
.
listener
(
it
);
ListeningStatus
.
LISTENING
})
}
}
/**
/**
...
@@ -163,7 +181,11 @@ inline fun <reified E : Event> CoroutineScope.subscribeOnce(
...
@@ -163,7 +181,11 @@ inline fun <reified E : Event> CoroutineScope.subscribeOnce(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
noinline
listener
:
suspend
E
.(
E
)
->
Unit
noinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
=
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
)
{
it
.
listener
(
it
);
ListeningStatus
.
STOPPED
})
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
,
Listener
.
ConcurrencyKind
.
LOCKED
)
{
it
.
listener
(
it
);
ListeningStatus
.
STOPPED
})
//
//
...
@@ -187,9 +209,14 @@ inline fun <reified E : Event> CoroutineScope.subscribeOnce(
...
@@ -187,9 +209,14 @@ inline fun <reified E : Event> CoroutineScope.subscribeOnce(
@OptIn
(
MiraiInternalAPI
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
)
inline
fun
<
reified
E
:
BotEvent
>
Bot
.
subscribe
(
inline
fun
<
reified
E
:
BotEvent
>
Bot
.
subscribe
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
concurrency
:
Listener
.
ConcurrencyKind
=
Listener
.
ConcurrencyKind
.
LOCKED
,
noinline
handler
:
suspend
E
.(
E
)
->
ListeningStatus
noinline
handler
:
suspend
E
.(
E
)
->
ListeningStatus
):
Listener
<
E
>
=
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
)
{
if
(
it
.
bot
===
this
)
it
.
handler
(
it
)
else
ListeningStatus
.
LISTENING
})
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
,
concurrency
)
{
if
(
it
.
bot
===
this
)
it
.
handler
(
it
)
else
ListeningStatus
.
LISTENING
})
/**
/**
...
@@ -207,9 +234,14 @@ inline fun <reified E : BotEvent> Bot.subscribe(
...
@@ -207,9 +234,14 @@ inline fun <reified E : BotEvent> Bot.subscribe(
@OptIn
(
MiraiInternalAPI
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
)
inline
fun
<
reified
E
:
BotEvent
>
Bot
.
subscribeAlways
(
inline
fun
<
reified
E
:
BotEvent
>
Bot
.
subscribeAlways
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
concurrency
:
Listener
.
ConcurrencyKind
=
Listener
.
ConcurrencyKind
.
CONCURRENT
,
noinline
listener
:
suspend
E
.(
E
)
->
Unit
noinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
{
):
Listener
<
E
>
{
return
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
)
{
if
(
it
.
bot
===
this
)
it
.
listener
(
it
);
ListeningStatus
.
LISTENING
})
return
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
,
concurrency
)
{
if
(
it
.
bot
===
this
)
it
.
listener
(
it
);
ListeningStatus
.
LISTENING
})
}
}
/**
/**
...
@@ -229,7 +261,7 @@ inline fun <reified E : BotEvent> Bot.subscribeOnce(
...
@@ -229,7 +261,7 @@ inline fun <reified E : BotEvent> Bot.subscribeOnce(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
noinline
listener
:
suspend
E
.(
E
)
->
Unit
noinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
=
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
)
{
E
::
class
.
subscribeInternal
(
Handler
(
coroutineContext
,
Listener
.
ConcurrencyKind
.
LOCKED
)
{
if
(
it
.
bot
===
this
)
{
if
(
it
.
bot
===
this
)
{
it
.
listener
(
it
)
it
.
listener
(
it
)
ListeningStatus
.
STOPPED
ListeningStatus
.
STOPPED
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventInternalJvm.kt
View file @
55ed5f9d
...
@@ -7,6 +7,8 @@
...
@@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
*/
@
file
:
Suppress
(
"unused"
)
package
net.mamoe.mirai.event.internal
package
net.mamoe.mirai.event.internal
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.CoroutineScope
...
@@ -21,11 +23,19 @@ import kotlin.coroutines.EmptyCoroutineContext
...
@@ -21,11 +23,19 @@ import kotlin.coroutines.EmptyCoroutineContext
@MiraiInternalAPI
@MiraiInternalAPI
@Suppress
(
"FunctionName"
)
@Suppress
(
"FunctionName"
)
fun
<
E
:
Event
>
Class
<
E
>.
_subscribeEventForJaptOnly
(
scope
:
CoroutineScope
,
onEvent
:
Function
<
E
,
ListeningStatus
>):
Listener
<
E
>
{
fun
<
E
:
Event
>
Class
<
E
>.
_subscribeEventForJaptOnly
(
scope
:
CoroutineScope
,
onEvent
:
Function
<
E
,
ListeningStatus
>):
Listener
<
E
>
{
return
this
.
kotlin
.
subscribeInternal
(
scope
.
Handler
(
EmptyCoroutineContext
)
{
onEvent
.
apply
(
it
)
})
return
this
.
kotlin
.
subscribeInternal
(
scope
.
Handler
(
EmptyCoroutineContext
,
Listener
.
ConcurrencyKind
.
CONCURRENT
)
{
onEvent
.
apply
(
it
)
})
}
}
@MiraiInternalAPI
@MiraiInternalAPI
@Suppress
(
"FunctionName"
)
@Suppress
(
"FunctionName"
)
fun
<
E
:
Event
>
Class
<
E
>.
_subscribeEventForJaptOnly
(
scope
:
CoroutineScope
,
onEvent
:
Consumer
<
E
>):
Listener
<
E
>
{
fun
<
E
:
Event
>
Class
<
E
>.
_subscribeEventForJaptOnly
(
scope
:
CoroutineScope
,
onEvent
:
Consumer
<
E
>):
Listener
<
E
>
{
return
this
.
kotlin
.
subscribeInternal
(
scope
.
Handler
(
EmptyCoroutineContext
)
{
onEvent
.
accept
(
it
);
ListeningStatus
.
LISTENING
;
})
return
this
.
kotlin
.
subscribeInternal
(
scope
.
Handler
(
EmptyCoroutineContext
,
Listener
.
ConcurrencyKind
.
CONCURRENT
)
{
onEvent
.
accept
(
it
);
ListeningStatus
.
LISTENING
;
})
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment