Dart Isolates - How to access a specific one? - dart

How can I create an Isolate n°2 from an Isolate n°1 and give it some sort of identification ? The goal would be to be able to communicate with it from Isolate n°3, despite that third Isolate not having a SendPort letting it talk to Isolate n°2.
Isolate n°3 would have to create a SendPort from the identification choosen.
EDIT :
I have found IsolateNameServer from the Flutter team, and https://github.com/dart-lang/sdk/issues/44495 mentions using the Registry functionality instead. Is there any resources/examples talking about these 2 solutions ?

I have not seen any direct way to attach an identity to a specific Isolate.
Isolates get IDs as soon as they're generated.
You may retrieve the specific ID of an Isolate by the example below:
import 'dart:isolate';
import 'dart:developer';
void isolateExample(var n){
// displaying 5*5 result and the specific Isolate's ID
print('Result is: ${n*n} and it has the Isolate ID: ');
print(Service.getIsolateID(Isolate.current));
}
void main() async{
// producing an Isolate to perform a computation 5*5
await Isolate.spawn(isolateExample,5);
}
// Output: 25 and **it has the Isolate ID: isolates/189015374367763**

Related

Dart How to load file in runtime

I'm writing a discord bot using the nyxx library and want use dynamic file import for load command info and handler. But, after 5 hours of searching with Google, I didn't find anything to help me do that.
In Node.js, I can use require() or import() for it: Does the dart have something like that?
A small code snippet, showing what I want do:
this.commands = new Collection();
fs.readdirSync('./src/commands').filter(( f ) => f.endsWith( '.js' )).forEach((file) => {
const command = require(`../commands/${file}`);
this.commands.set( command.info.name, command );
});
Is it possible to do this or not? (I don't like to write many imports for commands and register it in lib.)
You can in theory use Isolate.spawnUri to spawn external Dart programs to run in its own Isolate instances that can then communicate back to the main program using SendPort.
It does, however, come with some limitations. E.g. it is very limited what types of objects you can send though SendPort when using spawnUri since the two programs does not share any type information (compared to Isolate.spawn which does allow you to send your own custom types). The documented types you can send can be found here:
Null
bool
int
double
String
List or Map (whose elements are any of these)
TransferableTypedData
SendPort
Capability
https://api.dart.dev/stable/2.17.6/dart-isolate/SendPort/send.html
But it does allow us to make some kind of protocol and you can create some helper class around this to handle the conversion of a known object structure into e.g. Map<String, Object>.
A small example that works with Dart VM would be:
Your command implemented as: command.dart
import 'dart:isolate';
void main(List<String> arguments, Map<String, Object> message) {
final userName = message['username'] as String;
final sendPort = message['port'] as SendPort;
sendPort.send('Hi $userName. '
'You got a message from my external command program!');
}
Your server that calls your command: server.dart
import 'dart:isolate';
void main() {
final recievePort = ReceivePort();
recievePort.listen((message) {
print('Got the following message: $message');
recievePort.close();
});
Isolate.spawnUri(Uri.file('command.dart'), [], {
'username': 'julemand101',
'port': recievePort.sendPort,
});
}
If running this with: dart server.dart you, hopefully, get:
Got the following message: Hi julemand101. You got a message from my external command program!
If you want to compile your application, you can do so by doing the following. You need to compile the command.dart, since a compiled Dart program does not understand how to read Dart code.
dart compile exe server.dart
dart compile aot-snapshot command.dart
You should here change Uri.file('command.dart') to Uri.file('command.aot') since the file-extension for aot-snapshot are .aot.
If everything works, you should be able to see:
> .\server.exe
Got the following message: Hi julemand101. You got a message from my external command program!

flatMap vs map, basic explanation is ok, but what happens when my transformation function isn't synchronous by itself?

I like basic explanations of complex concepts in reactor all over the web, they are not particularly useful in production code, so following piece of code I wrote which sends a message to kafka using reactor kafka + spring boot:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.kafka.sender.KafkaSender;
import reactor.kafka.sender.SenderOptions;
import reactor.kafka.sender.SenderRecord;
import reactor.kafka.sender.SenderResult;
import java.util.Properties;
public class CallbackSender {
private ObjectMapper objectMapper;
private String topic;
private static final Logger log = LoggerFactory.getLogger(CallbackSender.class.getName());
private final KafkaSender<String, String> sender;
public CallbackSender(ObjectMapper objectMapper, Properties senderProps, String topic) {
this.sender = KafkaSender.create(SenderOptions.create(senderProps));
this.objectMapper = objectMapper;
this.topic = topic;
}
public Mono<SenderResult<String>> sendMessage(ProcessContext<? extends AbstractMessage> processContext) throws JsonProcessingException {
ProducerRecord<String, String> producerRecord = new ProducerRecord<>(topic,
objectMapper.writeValueAsString(processContext.getMessage()));
SenderRecord<String, String, String> senderRecord = SenderRecord.create(producerRecord, processContext.getId());
return sender.send(Flux.just(senderRecord))
.doOnError(e -> log.error("Send failed", e))
.last();
}
}
What I can't grasp in my mind is what exactly is the difference between calling this.sendMessage as .map vs .flatMap from the outer pipeline, so what for the explanation that map applying synchronous transformation to the emitted element if my synchronous function is not really doing anything synchronous apart from basic fields fetch?
Here Kafka sender is already reactive and async , so it doesn't matter which one I use? Is that correct assumption?
Is my code non-idiomatic?
Or for this particular it would be just a safe wrap of everything I am doing inside .sendMessage in .flatMap in case someone would add synchronous code in future, i.e. sugar-safety syntax.
My understanding is that .map will simply prepare pipeline in this case which returns Mono, and subscriber for outer calling pipeline will trigger entire domino effect, is that correct?
What I can't grasp in my mind is what exactly is the difference between calling this.sendMessage as .map vs .flatMap from the outer pipeline
map() applies a synchronous function (i.e. one "in-place" with no subscriptions or callbacks) and just returns the result as is. flatMap() applies an asynchronous transformer function, and unwraps the Publisher when done. So:
My understanding is that .map will simply prepare pipeline in this case which returns Mono, and subscriber for outer calling pipeline will trigger entire domino effect, is that correct?
Yes, that's correct (if by "domino effect" you mean that the returning mono will be subscribed to and its result returned.)
so what for the explanation that map applying synchronous transformation to the emitted element if my synchronous function is not really doing anything synchronous apart from basic fields fetch?
Quite simply, because that's what you've told it to do. There's nothing inherently asynchronous about setting up a publisher, just its execution once it's been subscribed to (which doesn't happen with a map() call.)

Dart Keyboard Event Command Line

I've been monkeying around with Dart (& Flutter more specifically on mobile) and have become quite interested in trying Flutter on Desktop.
Anyways, for this one app idea, I need the ability to create a key event. From my research, I found this: https://api.dartlang.org/stable/2.2.0/dart-html/KeyEvent-class.html which mentions a KeyEvent however this primarily relates to Dart:HTML (which I presume just means browser only).
Does Dart run in the command line support any ability for generating key events? Like say I wanted an app to type something for a user.
Thanks!
#Isaac has basically explained it in his comment but this is how it looks like in code:
import 'dart:io';
void main(){
stdin.echoMode = false;
stdin.lineMode = false;
while(true){
if(stdin.readByteSync() == 102){ // f
print('You payed respect');
}
else{break;}
}
}

What is the recommended way to get the total number of entities of a kind stored in the datastore in dart?

We are using the gcloud dart library to access the datastore. We would like to display the total number of users.
Of course, the simplest way to do this is:
db.query(User).run().length
but this would fetch all users.
Is there a way to run this query efficiently with the dart gcloud library? If not, will querying for all entities be a big performance issue and should we store the total number of users in a separate entity?
Google Cloud Datastore provides a number of special entity kinds which use the reserved __xxx__ names and can be used to query datastore metadata.
Using this mechanism it is possible to e.g. query all namespaces by using __namespace__, query all kinds by using __kind__. package:gcloud contains already the special Kind and Namespace kinds for this purpose.
Any other metadata kind can be just defined by the user, among others, for querying kind counts.
Here is a snippet which allows one to count entities in Dart:
import 'dart:async';
import 'package:appengine/appengine.dart';
import 'package:gcloud/db.dart';
Future main(List<String> args) async {
await withAppEngineServices(() async {
print(await getStats('Package'));
print(await getStats('PackageVersion'));
});
}
Future<Stats> getStats(String kind) async {
final query = dbService.query(Stats)..filter('kind_name =', kind);
final Stats stats = (await query.run().toList()).first;
return stats;
}
#Kind(name: '__Stat_Kind__', idType: IdType.String)
class Stats extends ExpandoModel {
#StringProperty(propertyName: 'kind_name')
String kindName;
#IntProperty()
int count;
String toString() => 'Stats(kind: "$kindName", count: $count)';
}

Is it possible to have multiple send and receive ports for Dart Isolates?

Is it possible to open a multiple send and receive ports for the same Isolate in Dart?
E.g. Following code sample would have created two isolates with each having its own send port. However, I was wondering if there is any way to create more than one send/receive ports for the same isolate and choose the receive port to send the message to.
#import('dart:isolate');
echo() {
}
main() {
var sendPort1 = spawnFunction(echo);
var sendPort2 = spawnFunction(echo);
}
You can actually create any number of ReceivePorts, and then as Matt said, any number of SendPorts per ReceivePort.
By default an isolate, including the main isolate, has a ReceivePort created and available through the port getter. This ReceivePort is connected to the SendPort returned from spawnFunction() and spawnUri(). But you can create a new ReceivePort with new ReceivePort(), then you can create as many connected SendPorts as you want via toSendPort(). To use them you send the new SendPort itself along with a message on the original SendPort you either got from spawnFunction(), or via ReceivePort.receive().
By doing this you can set up multiple "channels" between two isolates. I haven't played with it yet to see how this works in practice yet, I've been multiplexing channels via structured messages on one SendPort.
Note that you can create a ReceivePort in any isolate: the parent isolate or the child isolate. So if you want the partent to have two SendPorts to the child, then you need one from spawnFunction() and another that's passed from the child back to the parent in a message.
Here's your example changed to use multiple SendPorts. The steps:
main: Spawn an isolate
main: Send a message with a SendPort so that the isolate can send a message back
echo: Create a second ReceivePort in the isolate
echo: Receive a message in the isolate with a replyTo SendPort
echo: Create a SendPort from the ReceivePort and send it back
main: Receive the message and SendPort from echo
Now main() has two independent SendPorts to the isolate.
#import('dart:isolate');
echo() {
var port2 = new ReceivePort(); // 3
port2.receive((m, p) {
print("two: $m");
});
port.receive((m, p) { // 4
print("one: $m");
p.send("ack", port2.toSendPort()); // 5
});
}
main() {
port.receive((m, sendPort2) { // 6
sendPort2.send("hello 2");
});
var sendPort1 = spawnFunction(echo); // 1
sendPort1.send("hello 1", port.toSendPort()); // 2
}
This prints:
one: hello 1
two: hello 2
Whew!
While I'm not sure about multiple receive ports. You can create multiple send ports per receive port. This functionality is build into the ReceivePort class: ReceivePort.toSendPort
As indicated at the bottom of the help:
It is legal to create several SendPorts from the same ReceivePort.
Hope this helps.
The answer from Justin is basically right, but caused me some trouble because the isolate stopped responding after step 5 has been executed. So here is an updated version that actually worked in my context:
import 'dart:isolate';
echo() {
var port2 = new ReceivePort();
port2.receive((m, p) {
print("two: $m");
});
port.receive((m, p) {
print("one: $m");
p.send(port2.toSendPort());
});
}
main() {
var sendPort1 = spawnFunction(echo);
sendPort1.call("hello 1").then((newPort)=>newPort.send("hello 2"));
}
The major difference is simply that the port is send as message rather than using the replyTo field. This also allows for a more compact code as the other receiving Port is not required.

Resources