|
| 1 | +# RD |
| 2 | +New child process communication involves 3 different things: |
| 3 | +1. Lifetimes |
| 4 | +2. Rd entities |
| 5 | +3. Rdgen |
| 6 | + |
| 7 | +Let's dive in each of them: |
| 8 | + |
| 9 | +## Lifetimes |
| 10 | + |
| 11 | +Imagine an object having some resources that should be freed |
| 12 | +when object dies. For this purpose Java introduced interfaces |
| 13 | +```Closeable\AutoCloseable```. Also, 2 helper functions were introduced: try-with-resources and Kotlin's ```Closeable.use```. There are several problems: |
| 14 | +1. Object's lifetime can be more complicated that ```Closeable.use``` scope. |
| 15 | +2. If function parameter is ```Closeable``` - should you close it? |
| 16 | +3. Multiple closes. |
| 17 | +4. Concurrent closes. |
| 18 | +5. If you have several objects that depends on another object's lifetime - how to correctly close all of them with respect to issues 1-4? How to make it simple, convenient and fast? |
| 19 | + |
| 20 | +And so Lifetime was introduced. |
| 21 | +### Lifetime: |
| 22 | +```Lifetime``` is a class, where you can register callbacks and which can be terminated once, thus executing all registered callbacks. |
| 23 | + |
| 24 | +```Lifetime``` is an abstract class, it's inheritor - ```LifetimeDefinition```. The only difference - only ```LifetimeDefinition``` can be terminated. Though all ```Lifetime``` are instances of ```LifetimeDefinition```, there are some conventions: |
| 25 | +1. Do not cast ```Lifetime``` to ```LifetimeDefinion``` unless you are the one who created ```LifetimeDefinition```. |
| 26 | +2. If you introduce somewhere ```LifetimeDefinition``` - either attach it to another ```Lifetime``` or provide code that terminates it. |
| 27 | + |
| 28 | +Useful ```Lifetime``` methods: |
| 29 | + |
| 30 | +- ```onTermination``` - Executes lambda/closeable when lifetime terminates. If already terminated - executes instantly. Termination will proceed on thread that called ```LifetimeDefinition.terminate()```. Callbacks will be executed in ***reversed order***, that is LIFO - last added callback will be executed first. |
| 31 | +- ```onTerminationIfAlive``` - same as ```OnTermination```, but callback will not be executed if lifetime is not alive. |
| 32 | +- ```executeIfAlive``` - executes lambda if lifetime is alive. This method guarantees that lifetime will not be terminated until lambda completes. |
| 33 | +- ```createdNested``` - creates child LifetimeDefinition that will be terminated if parent does. |
| 34 | +- ```usingNested``` - same as ```createNested```, but like ```Closeable.use``` pattern. |
| 35 | +- ```Eternal``` - lifetime that never terminates. |
| 36 | +- ```Terminated``` - lifetime that already terminated. |
| 37 | +- ```status``` - see [LifetimeStatus.kt](https://github.com/JetBrains/rd/blob/9b806ccc770515f6288c778581c54607420c16a7/rd-kt/rd-core/src/main/kotlin/com/jetbrains/rd/util/lifetime/LifetimeStatus.kt) in RD repo. There are 3 convenient method: ```IsAlive, IsNotAlive, IsTerminated```. |
| 38 | + |
| 39 | +### LifetimeDefinition: |
| 40 | +- ```terminate``` - terminates ```Lifetime``` and calls all callbacks. Sometimes if multiple concurrent terminations occurred - method will return before executing all callbacks because some other thread is doing this. |
| 41 | + |
| 42 | +## Rd |
| 43 | +Rd is a cross-language, cross-platform, light-weight, reactive, one-to-one rpc protocol. Can work either on the same or different machines via internet. |
| 44 | + |
| 45 | +Useful entities: |
| 46 | +- ```Protocol``` - encapsulation of all logic regarding rd communication. All entities should be bound to protocol before using. Contains ```IScheduler``` which executes runnables on different thread. |
| 47 | +- ```RdSignal``` - entity for ___fire and forget___. You can add callback for every received message via ```advise(lifetime, callback)``` method. There are 2 interfaces - ```ISink``` which allows only to advise for messages, and ```ISignal``` which can also ```fire``` events. Also, there is just ```Signal``` class with same behaviour, but without remote communication. |
| 48 | + |
| 49 | + ___Important___: if you advise and fire from the same process - your callback will receive ___not only___ messages from another process, but also the ones you fire. |
| 50 | +- ```RdProperty``` - stateful property. You can get current value, you can advise - advised callback will be executed on current value and on every change. |
| 51 | +- ```RdCall``` - remote procedure call. |
| 52 | + |
| 53 | +Also there are ```RdSet```, ```RdMap``` and many other. |
| 54 | + |
| 55 | +There is ```async``` property that allows you to ```fire``` entities from any thread. Otherwise you would need to do it from ```Protocol.scheduler``` thread. All rd entities should be at first |
| 56 | + |
| 57 | +## Rdgen |
| 58 | +Generates custom classes and requests which can be bound to protocol and advised. There is special model DSL for it. |
| 59 | +### Model DSL |
| 60 | +Eexample: |
| 61 | +1. [Korifey](https://github.com/korifey/rd_example/blob/main/src/main/kotlin/org/korifey/rd_example/model/Root.kt) - quite simple |
| 62 | +2. [Rider Unity plugin](https://github.com/JetBrains/resharper-unity/tree/net223/rider/protocol/src/main/kotlin/model) - complicated one |
| 63 | + |
| 64 | +First you need to define ```Root``` object - only one instance of each root can be assigned to protocol. |
| 65 | + |
| 66 | +Then there is root extension ```Ext(YourRoot)``` where you can define your own types and model entities. You can assign multiple extensions of root for the protocol. Auxillary structures can be defined as direct fields - they will be also generated. |
| 67 | + |
| 68 | +DSL: |
| 69 | +- ```structdef``` - structure with fields that cannot be bound to protocol, but can be serialized. Can be open for inheritace - ```openstruct```, can be abstract - ```basestruct```. Can have only ```field``` as member. |
| 70 | +- ```classdef``` - class that can be bould to model. Can also have ```property```, ```signal```, ```call``` e.t.c. as members. It is possible to do inheritance - ```openclass```, ```baseclass```. |
| 71 | +- ```interfacedef``` - to define interfaces. Use ```method``` to create signature. |
| 72 | + |
| 73 | + You can use ```extends``` and ```implements``` to work with inheritance. |
| 74 | + |
| 75 | + ___N.B.___ - rdgen can generate models also for C# and C++. Their structs and classes have a little different behaviour. |
| 76 | +- Rd entities - only in bindable models(```Ext```, ```classdef```): |
| 77 | + - ```property``` |
| 78 | + - ```signal``` |
| 79 | + - ```source``` |
| 80 | + - ```sink``` |
| 81 | + - ```array``` and ```immutablelist``` |
| 82 | +- Useful properties in dsl entities: |
| 83 | + - async - as ```async``` in RD entities |
| 84 | + - docs - provides kdoc/javadoc for generated entity |
| 85 | +### Gradle |
| 86 | +[Example](https://github.com/korifey/rd_example/blob/main/build.gradle) |
| 87 | + |
| 88 | +```RdGenExtension``` configurates Rdgen, useful properties: |
| 89 | +- ```sources``` - folders with dsl .kt files. If not present, scan classpath for inheritors of ```Root``` and ```Ext```. |
| 90 | +- ```hashfile``` - folder to store hash file ```.rdgen``` for incremental generation. |
| 91 | +- ```packages``` - java package names to search toplevels, delimited by ','. Example: ```com.jetbrains.rd.model.nova,com,org```. |
| 92 | +- Configuring model generation with method ```RdGenExtension.generator```: |
| 93 | + - root - for which root you are declaring this generator |
| 94 | + - namespace - which namespace should be used in generated source. In kotlin it configures generated packaged name. |
| 95 | + - directory - where to put generated files. |
| 96 | + - transform - can be ```symmetric```, ```asis``` and ```reversed```. This allows to configure model interface differently for client-server scenarios. |
| 97 | + |
| 98 | + P.S. This is legacy from distant past, in 99% of time you should use ```symmetric```. In 1% chance - consult with somebody if you really need another option. |
| 99 | + - language - can be ```kotlin```, ```cpp``` and ```csharp```. |
| 100 | + |
| 101 | +## UtBot project |
| 102 | + |
| 103 | +There is another gradle project ```utbot-rd``` which contains model sources in ```rdgenModels``` sources. Look for ```org.utbot.rd.models.ProtocolRoot```. |
| 104 | + |
| 105 | +Usefull: |
| 106 | +1. if you need to use rd somewhere - add following dependencies: |
| 107 | + ``` |
| 108 | + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: 'actual.version' |
| 109 | + |
| 110 | + implementation group: 'com.jetbrains.rd', name: 'rd-core', version: 'actual.version' |
| 111 | + ``` |
| 112 | +2. There are some usefull classes to work with processes & rd: |
| 113 | + - ```LifetimedProcess``` - binds ```Lifetime``` to process. If process dies - lifetime terminates and vice versa. You can terminate lifetime manually - this will destroy process. |
| 114 | + - ```ProcessWithRdServer``` - also starts Rd server and waits for connection. |
| 115 | + - ```UtInstrumentationProcess``` - encapsulates logic for preparing child process for executing arbitary commands. Exposes ```protocolModel``` for communicating with child process. |
| 116 | + - ```ConcreteExecutor``` is convenient wrapper for executing commands and managing resources. |
| 117 | +3. How child communication works: |
| 118 | + - Choosing free port |
| 119 | + - Creating child process, passing port as argument |
| 120 | + - Both processes create protocols and bind model |
| 121 | + - Child process setups all callbacks |
| 122 | + - Parent process cannot send messages before child creates protocol, otherwise messages will be lost. So child process needs to signal that he is ready. |
| 123 | + - Child proces creates special file in temp dir, that is observed by parent process. |
| 124 | + - When parent process spots file - he deletes it, and then sends special message for preparing child proccess instrumentation |
| 125 | + - Only then process is ready for executing commands |
| 126 | +4. How to write custom commands for child process |
| 127 | + - Add new ```call``` in ```ProtocolModel``` |
| 128 | + - Regenerate models |
| 129 | + - Add callback for new ```call``` in ```ChildProcess.kt``` |
| 130 | + - Use ```ConcreteExecutor.withProcess``` method |
| 131 | + - ___Important___ - do not add `Rdgen` as implementation dependency, it breaks some `.jar`s as it contains `kotlin-compiler-embeddable`. |
| 132 | +5. Logs |
| 133 | +
|
| 134 | + There is ```UtRdLogger``` where you can configure level via ```log4j2.xml```. |
| 135 | +
|
| 136 | +6. Custom protocol marshalling types |
| 137 | +
|
| 138 | + Do not spend time on it until: |
| 139 | + - Cyclic dependencies removed from UtModels |
| 140 | + - Kotlinx.serialization is used |
| 141 | +
|
0 commit comments