actors example - stock server

This commit is contained in:
Alex Tkachman
2012-02-10 11:37:18 +02:00
parent 5265938826
commit a6fdd0fd05
12 changed files with 1090 additions and 190 deletions
-186
View File
@@ -1,186 +0,0 @@
package actors
import std.concurrent.*
import std.util.*
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
import java.util.Date
/*
Message handling class which process only one message at any given moment
Three main ways to use it
- post and forget
- post and await result
- post and assign callback to be called when message processed
*/
abstract class Actor(protected val executor: Executor) {
// we can not do it private or protected
// otherwise updater defined in class object will not be able to access it
volatile var queue : FunctionalQueue<Any> = emptyQueue
/*
Handle message and return result
This method guaranteed to be executed only one per object at any given time
*/
protected abstract fun onMessage(message: Any) : Any?
/*
Post message to the actor.
The method returns immediately and the message itself will be processed later
*/
fun post(message: Any) {
while(true) {
val q = queue
if(q.empty) {
if(queueUpdater.compareAndSet(this, q, busyEmptyQueue)) {
executor.execute { process(message) }
break
}
}
else {
val newQueue = (if(q == busyEmptyQueue) emptyQueue else q) add message
if(queueUpdater.compareAndSet(this, q, newQueue)) {
break
}
}
}
}
/*
Post message to the actor and schedule callback to be executed on given executor when message processed
*/
fun post(message: Any, executor: Executor = this.executor, callback: (Any?)->Unit) =
post(Callback(message, executor, callback))
/*
Send message to the actor and await result
*/
fun send(message: Any) : Any? {
val request = Request(message)
post(request)
request.await()
return request.result
}
private fun nextMessage() {
while(true) {
val q = queue
if(q == busyEmptyQueue) {
if(queueUpdater.compareAndSet(this, busyEmptyQueue, emptyQueue)) {
break;
}
}
else {
val removed = q.removeFirst()
val newQueue = if(removed._2.empty) busyEmptyQueue else removed._2
if(queueUpdater.compareAndSet(this, q, newQueue)) {
executor.execute { process(removed._1) }
break;
}
}
}
}
private fun process(message: Any) {
when(message) {
is Request -> {
message.result = onMessage(message.message)
message.countDown()
}
is Callback -> {
val result = onMessage(message.message)
message.executor execute {
val callback = message.callback;
callback(result)
}
}
else -> onMessage(message)
}
nextMessage()
}
/*
Create actor on the same executor
*/
fun actor(handler: (Any)->Any?) = executor.actor(handler)
class object {
val queueUpdater = AtomicReferenceFieldUpdater.newUpdater(typeinfo<Actor>.javaClassForType,typeinfo<FunctionalQueue<Any>>().javaClassForType, "queue").sure()
val emptyQueue = FunctionalQueue<Any>()
val busyEmptyQueue = FunctionalQueue<Any>() add "busy empty queue"
class Request(val message: Any) : java.util.concurrent.CountDownLatch(1) {
var result: Any? = null
}
class Callback(val message: Any, val executor: Executor, val callback: (Any?) -> Unit)
}
}
fun Executor.actor(handler: (Any)->Any?) : Actor = object: Actor(this) {
override fun onMessage(message: Any) {
handler(message)
}
}
fun singleThreadActor(handler: (Any)->Any?) : Actor = Executors.newSingleThreadExecutor().sure().actor(handler)
class App() : Actor(Executors.newFixedThreadPool(10).sure()) {
private val logger = executor.actor { message ->
println("${Date()}:\t\t$message")
}
private val actors = Array<Actor>(100, { createChild(it) });
private var finishedChildren = actors.size
override fun onMessage(message: Any) {
when(message) {
"start" -> {
logger post "app started"
for(a in actors) {
a post "start"
}
}
"child finished" -> {
if(--finishedChildren == 0) {
logger send "app finished"
(executor as ExecutorService).shutdown()
}
}
else -> {
logger post "unknown message $message"
}
}
}
private fun createChild(index: Int) : Actor = actor { message ->
val next = (index + 1) % actors.size
when(message) {
"start" -> {
logger post "$index started"
actors[next] post #(index, 0)
}
is Tuple2<Int,Int> -> {
logger post "$index received $message"
val from = message._1
val value = message._2
if(next != from) {
actors[next] post #(from, value+1)
}
else {
logger post "$index finished"
this@App post "child finished"
}
}
else -> {}
}
}
}
fun main(args: Array<String>) {
App() post "start"
}
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinRuntime" level="project" />
</component>
</module>
+173
View File
@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AntConfiguration">
<defaultAnt bundledAnt="true" />
</component>
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="?*.properties" />
<entry name="?*.xml" />
<entry name="?*.gif" />
<entry name="?*.png" />
<entry name="?*.jpeg" />
<entry name="?*.jpg" />
<entry name="?*.html" />
<entry name="?*.dtd" />
<entry name="?*.tld" />
<entry name="?*.ftl" />
</wildcardResourcePatterns>
<annotationProcessing enabled="false" useClasspath="true" />
</component>
<component name="CopyrightManager" default="">
<module2copyright />
</component>
<component name="DependencyValidationManager">
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</component>
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/actors.iml" filepath="$PROJECT_DIR$/actors.iml" />
</modules>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
<component name="libraryTable">
<library name="KotlinRuntime">
<CLASSES>
<root url="file://$PROJECT_DIR$/../../../dist/kotlinc/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
<jarDirectory url="file://$PROJECT_DIR$/../../../dist/kotlinc/lib" recursive="false" />
</library>
</component>
</project>
+523
View File
@@ -0,0 +1,523 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="9624598b-68cd-4d51-9271-5ac61ef5a43a" name="Default" comment="" />
<ignored path="actors.iws" />
<ignored path=".idea/workspace.xml" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" />
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints />
</component>
<component name="DebuggerManager">
<breakpoint_any>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
</breakpoint_any>
<breakpoint_rules />
<ui_properties />
</component>
<component name="FavoritesManager">
<favorites_list name="actors" />
</component>
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="Main.kt" pinned="false" current="true" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/Main.kt">
<provider selected="true" editor-type-id="text-editor">
<state line="32" column="57" selection-start="881" selection-end="881" vertical-scroll-proportion="1.3675214">
<folding />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="ProjectFrameBounds">
<option name="y" value="22" />
<option name="width" value="1440" />
<option name="height" value="874" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
</navigator>
<panes>
<pane id="Scope">
<subPane subId="Project Files">
<PATH>
<PATH_ELEMENT USER_OBJECT="Root">
<option name="myItemId" value="" />
<option name="myItemType" value="" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="src" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="PackagesPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="actors" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="project.structure.last.edited" value="Modules" />
<property name="GoToFile.includeJavaFiles" value="false" />
<property name="project.structure.proportion" value="0.15" />
<property name="GoToClass.toSaveIncludeLibraries" value="false" />
<property name="recentsLimit" value="5" />
<property name="MemberChooser.sorted" value="false" />
<property name="MemberChooser.showClasses" value="true" />
<property name="project.structure.side.proportion" value="0.2" />
<property name="GoToClass.includeLibraries" value="false" />
<property name="dynamic.classpath" value="false" />
<property name="MemberChooser.copyJavadoc" value="false" />
</component>
<component name="RunManager" selected="Kotlin.org.jetbrains.kotlin.examples.actors">
<configuration default="false" name="org.jetbrains.kotlin.examples.actors" type="JetRunConfigurationType" factoryName="Kotlin" temporary="true">
<option name="MAIN_CLASS_NAME" value="org.jetbrains.kotlin.examples.actors.namespace" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="actors" />
<envs />
<RunnerSettings RunnerId="Run" />
<ConfigurationWrapper RunnerId="Run" />
<method />
</configuration>
<configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin">
<module name="" />
<option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m" />
<option name="PROGRAM_PARAMETERS" />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="JetRunConfigurationType" factoryName="Kotlin">
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="actors" />
<envs />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="TestNG" factoryName="TestNG">
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="SUITE_NAME" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="GROUP_NAME" />
<option name="TEST_OBJECT" value="CLASS" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="OUTPUT_DIRECTORY" />
<option name="ANNOTATION_TYPE" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="moduleWithDependencies" />
</option>
<option name="USE_DEFAULT_REPORTERS" value="false" />
<option name="PROPERTIES_FILE" />
<envs />
<properties />
<listeners />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<module name="" />
<option name="MAIN_CLASS_NAME" />
<option name="HTML_FILE_NAME" />
<option name="HTML_USED" value="false" />
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<option name="VM_PARAMETERS" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="" />
<envs />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="moduleWithDependencies" />
</option>
<envs />
<patterns />
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="Kotlin.org.jetbrains.kotlin.examples.actors" />
</list>
<configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
<Host>localhost</Host>
<Port>5050</Port>
</configuration>
</component>
<component name="ShelveChangesManager" show_recycled="false" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="9624598b-68cd-4d51-9271-5ac61ef5a43a" name="Default" comment="" />
<created>1328866126960</created>
<updated>1328866126960</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="0" y="22" width="1440" height="874" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="CodeWindow" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32897604" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Gradle" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" />
<window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.18175288" sideWeight="0.50199205" order="0" side_tool="false" content_ui="combo" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.49800798" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="ResolveWindow" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
</layout>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="VcsManagerConfiguration">
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="CHECK_NEW_TODO" value="true" />
<option name="myTodoPanelSettings">
<value>
<are-packages-shown value="false" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="false" />
</value>
</option>
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="true" />
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="true" />
<option name="PERFORM_EDIT_IN_BACKGROUND" value="true" />
<option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true" />
<option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" />
<option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false" />
<option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false" />
<option name="ENABLE_BACKGROUND_PROCESSES" value="false" />
<option name="CHANGED_ON_SERVER_INTERVAL" value="60" />
<option name="SHOW_ONLY_CHANGED_IN_SELECTION_DIFF" value="true" />
<option name="CHECK_COMMIT_MESSAGE_SPELLING" value="true" />
<option name="DEFAULT_PATCH_EXTENSION" value="patch" />
<option name="SHORT_DIFF_HORISONTALLY" value="true" />
<option name="SHORT_DIFF_EXTRA_LINES" value="2" />
<option name="SOFT_WRAPS_IN_SHORT_DIFF" value="true" />
<option name="INCLUDE_TEXT_INTO_PATCH" value="false" />
<option name="INCLUDE_TEXT_INTO_SHELF" value="false" />
<option name="CREATE_PATCH_EXPAND_DETAILS_DEFAULT" value="true" />
<option name="SHOW_FILE_HISTORY_DETAILS" value="true" />
<option name="FORCE_NON_EMPTY_COMMENT" value="false" />
<option name="LAST_COMMIT_MESSAGE" />
<option name="MAKE_NEW_CHANGELIST_ACTIVE" value="false" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
<option name="ACTIVE_VCS_NAME" />
<option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
<option name="UPDATE_GROUP_BY_CHANGELIST" value="false" />
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager />
</component>
<component name="antWorkspaceConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="FILTER_TARGETS" value="false" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/Main.kt">
<provider selected="true" editor-type-id="text-editor">
<state line="32" column="57" selection-start="881" selection-end="881" vertical-scroll-proportion="1.3675214">
<folding />
</state>
</provider>
</entry>
</component>
<component name="masterDetails">
<states>
<state key="ArtifactsStructureConfigurable.UI">
<settings>
<artifact-editor />
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="FacetStructureConfigurable.UI">
<settings>
<last-edited>No facets are configured</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="GlobalLibrariesConfigurable.UI">
<settings>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="JdkListConfigurable.UI">
<settings>
<last-edited>1.6</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ModuleStructureConfigurable.UI">
<settings>
<last-edited>actors</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.6</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
<state key="ProjectLibrariesConfigurable.UI">
<settings>
<last-edited>KotlinRuntime</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>
+184
View File
@@ -0,0 +1,184 @@
package org.jetbrains.kotlin.examples.actors
import std.concurrent.*
import std.util.*
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
import java.util.Date
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.AtomicLong
/*
Message handling class which process only one message at any given moment
Three main ways to use it
- post and forget
- post and await result
- post and assign callback to be called when message processed
*/
abstract class Actor(protected val executor: Executor, val fair: Boolean = false) {
private val queue = Ref()
class Ref() : AtomicReference<FunctionalQueue<Any>>(emptyQueue), Runnable {
override fun run() {
if (!fair)
runUnfair()
else
runFair()
}
private fun runFair() {
while(true) {
val q = get()
if(q.identityEquals(busyEmptyQueue)) {
if(compareAndSet(busyEmptyQueue, emptyQueue)) {
break
}
}
else {
val removed = q.removeFirst()
val newQueue = if(removed._2.empty) busyEmptyQueue else removed._2
if(compareAndSet(q, newQueue)) {
doProcess(removed._1)
executor.execute(this)
break
}
}
}
}
private fun runUnfair() {
while (true) {
val q = get()
if(q.identityEquals(busyEmptyQueue)) {
if (compareAndSet(q, emptyQueue)) {
break
}
}
else {
if (compareAndSet(q, busyEmptyQueue)) {
var l = q.output
while (!l.empty) {
doProcess(l.head)
l = l.tail
}
l = q.input.reversed()
while (!l.empty) {
doProcess(l.head)
l = l.tail
}
}
}
}
}
override fun toString(): String? = null
}
/*
Handle message and return result
This method guaranteed to be executed only one per object at any given time
*/
protected abstract fun onMessage(message: Any) : Any?
/*
Post message to the actor.
The method returns immediately and the message itself will be processed later
*/
fun post(message: Any) {
messagesSent.incrementAndGet()
while(true) {
val q = queue.get()
val newQueue = (if (q.identityEquals(busyEmptyQueue)) emptyQueue else q) add message
if(queue.compareAndSet(q, newQueue)) {
if (q.identityEquals(emptyQueue)) {
executor.execute(queue)
}
break
}
}
}
fun postFirst(message: Any) {
messagesSent.incrementAndGet()
while(true) {
val q = queue.get()
val newQueue = (if (q.identityEquals(busyEmptyQueue)) emptyQueue else q) addFirst message
if(queue.compareAndSet(q, newQueue)) {
if (q.identityEquals(emptyQueue)) {
executor.execute(queue)
}
break
}
}
}
/*
Post message to the actor and schedule callback to be executed on given executor when message processed
*/
fun post(message: Any, executor: Executor = this.executor, callback: (Any?)->Unit) =
post(Callback(message, executor, callback))
/*
Send message to the actor and await result
*/
fun send(message: Any) : Any? {
val request = Request(message)
post(request)
request.await()
return request.result
}
private fun doProcess(message: Any) {
messagesProcessed.incrementAndGet()
when(message) {
is Request -> {
message.result = onMessage(message.message)
message.countDown()
}
is Callback -> {
val result = onMessage(message.message)
message.executor execute {
val callback = message.callback;
callback(result)
}
}
else -> onMessage(message)
}
}
/*
Create actor on the same executor
*/
fun actor(handler: (Any)->Any?) = executor.actor(handler)
class object {
val emptyQueue = FunctionalQueue<Any>()
val busyEmptyQueue = FunctionalQueue<Any>() add "busy empty queue"
class Request(val message: Any) : java.util.concurrent.CountDownLatch(1) {
var result: Any? = null
}
class Callback(val message: Any, val executor: Executor, val callback: (Any?) -> Unit)
val messagesSent = AtomicLong()
val messagesProcessed = AtomicLong()
val timer = fixedRateTimer(daemon=true, period=5000.lng) {
val sent = messagesSent.get()
val received = messagesProcessed.get()
println("Actors stat: Sent: $sent Processed: $received Pending: ${sent-received}")
}
}
}
fun Executor.actor(handler: (Any)->Any?) : Actor = object: Actor(this) {
override fun onMessage(message: Any) {
handler(message)
}
}
fun singleThreadActor(handler: (Any)->Any?) : Actor = Executors.newSingleThreadExecutor().sure().actor(handler)
+32
View File
@@ -0,0 +1,32 @@
package org.jetbrains.kotlin.examples.actors
import java.util.concurrent.Executors
fun main(args: Array<String>) {
val numberOfSymbols = 10000
val numberOfShards = 10
val numberOfClients = 100000
val stocksPerClient = 4
val stockServer = StockServer(numberOfShards)
for (i in 0..numberOfSymbols)
stockServer post RegisterStock(i.toString())
println("Stock server started")
val executor = Executors.newFixedThreadPool(4*Runtime.getRuntime().sure().availableProcessors()).sure()
for(i in 0..numberOfClients) {
val client = executor.actor{ message ->
// println(message)
}
for (k in 1..stocksPerClient) {
val stock = (Math.random() * numberOfSymbols).int
stockServer post Subscribe(stock.toString(), client)
}
}
println("clients connected")
}
+23
View File
@@ -0,0 +1,23 @@
package org.jetbrains.kotlin.examples.actors
import std.concurrent.*
import java.util.concurrent.Executor
import java.util.LinkedList
import java.util.concurrent.Executors
import java.util.HashMap
import java.util.HashSet
open class StockMessage(val symbol: String) {
class object {
val UpdateModel = "update"
val DoUpdateModel = "do update"
}
}
class RegisterStock(symbol: String) : StockMessage(symbol)
class Subscribe(symbol: String, val subscriber: Actor) : StockMessage(symbol)
class Unsubscribe(symbol: String, val subscriber: Actor) : StockMessage(symbol)
class PriceUpdate(symbol: String, val price: Double) : StockMessage(symbol)
+34
View File
@@ -0,0 +1,34 @@
package org.jetbrains.kotlin.examples.actors
import std.concurrent.*
import org.jetbrains.kotlin.examples.actors.Actor
import java.util.concurrent.Executors
import java.util.LinkedList
class StatCalculator() : Actor(Executors.newSingleThreadExecutor().sure()) {
val list = LinkedList<Long> ()
var average = 0.lng
var sum = 0.lng
var cnt = 0.lng
val timer = fixedRateTimer(period=2000.lng, daemon=true) {
this@StatCalculator post "print"
}
override fun onMessage(msg: Any) {
when(msg) {
is Long -> {
list.add(msg)
sum += msg
if (list.size() > 20)
sum -= list.removeFirst()
average = sum / list.size()
cnt++
}
"print" -> {
println("Avr. period:[${average}ms] # updates:[$cnt]")
}
else -> {}
}
}
}
+22
View File
@@ -0,0 +1,22 @@
package org.jetbrains.kotlin.examples.actors
import std.concurrent.*
import java.util.concurrent.Executor
import java.util.LinkedList
import java.util.concurrent.Executors
import java.util.HashMap
import java.util.HashSet
class Stock(val symbol: String) {
val subscribers = HashSet<Actor>()
var price = 10*Math.random()
fun update() {
price += Math.random() - 0.5
val priceUpdate = PriceUpdate(symbol, price)
for(s in subscribers) {
s post priceUpdate
}
}
}
+38
View File
@@ -0,0 +1,38 @@
package org.jetbrains.kotlin.examples.actors
import std.concurrent.*
import java.util.concurrent.Executor
import java.util.LinkedList
import java.util.concurrent.Executors
import java.util.HashMap
import java.util.HashSet
class StockServer(val numberOfShards : Int) : Actor(Executors.newFixedThreadPool(numberOfShards+1).sure()) {
private val stockToServer = HashMap<String,StockServerShard> ()
private val statCalculator = StatCalculator()
private val shards = Array<StockServerShard> (numberOfShards, { (i: Int) -> StockServerShard(statCalculator, executor) })
private val timer = fixedRateTimer(period=1000.lng, daemon=true) {
for(s in shards)
s post StockMessage.UpdateModel
}
override fun onMessage(message: Any): Unit {
when (message) {
is RegisterStock -> {
val shard = shards[stockToServer.size() % shards.size]
stockToServer.put(message.symbol, shard)
shard post message
}
is Subscribe -> {
stockToServer.get(message.symbol)?.post(message)
}
is Unsubscribe -> {
stockToServer.get(message.symbol)?.post(message)
}
else -> {}
}
}
}
@@ -0,0 +1,44 @@
package org.jetbrains.kotlin.examples.actors
import std.concurrent.*
import java.util.concurrent.Executor
import java.util.LinkedList
import java.util.concurrent.Executors
import java.util.HashMap
import java.util.HashSet
class StockServerShard(val statCalculator : StatCalculator, executor: Executor) : Actor(executor) {
private val stocks = HashMap<String,Stock>();
private var previousTime = System.currentTimeMillis();
private var updateScheduled = false
override fun onMessage(message: Any) {
when(message) {
StockMessage.DoUpdateModel -> {
val timeMillis = System.currentTimeMillis()
for(s in stocks.values()) {
s.update()
}
statCalculator post (timeMillis - previousTime)
previousTime = timeMillis
updateScheduled = false
}
StockMessage.UpdateModel -> {
if (!updateScheduled) {
this post StockMessage.DoUpdateModel
updateScheduled = true
}
}
is RegisterStock ->
stocks.put(message.symbol, Stock(message.symbol))
is Subscribe ->
stocks.get(message.symbol)?.subscribers?.add(message.subscriber)
is Unsubscribe ->
stocks.get(message.symbol)?.subscribers?.add(message.subscriber)
else -> {}
}
}
}