I am trying to use dart isolate library to improve my application performance.
Look at following code:
import 'dart:isolate';
import 'package:dbcrypt/dbcrypt.dart';
main() {
var pwConPort = new ReceivePort();
pwConPort.listen((data) {
print(data);
pwConPort.close();
}, onError: (err) {
print(err);
});
Isolate.spawn(generatePasswordConcurrency, pwConPort.sendPort);
}
void generatePasswordConcurrency(SendPort sendPort) {
sendPort.send(_generateHashPassword('Passsowr1222!'));
}
String _generateHashPassword(String password) {
var regex = new RegExp(r'^.*(?=.{7,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).*$');
if (!regex.hasMatch(password)) {
throw new StateError('Errors');
}
return new DBCrypt().hashpw(password, new DBCrypt().gensalt());
}
Everything works fine but i can only pass a static password, or better to say, i don't know, how to pass something dynamically. Here you can see, password is hardcoded, but i want to pass a variable for example.
void generatePasswordConcurrency(SendPort sendPort) {
sendPort.send(_generateHashPassword('Passsowr1222!'));
}
If the method _generateHashPassword will throw an error, how can I handling this error? I try to catch the error on listen method from ReceivePort
pwConPort.listen((data) {
print(data);
pwConPort.close();
}, onError: (err) {
print(err);
});
but still got unhandling exceptions message.
Observatory listening on http://127.0.0.1:51433
in ShutdownIsolate: Unhandled exception:
Bad state: Errors
#0 _generateHashPassword (file:///D:/Dart/samples/bin/isolate_error.dart:26:9)
#1 generatePasswordConcurrency (file:///D:/Dart/samples/bin/isolate_error.dart:19:40)
#2 _startIsolate.isolateStartHandler (dart:isolate-patch/isolate_patch.dart:221)
#3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:124)
Conclusion my question:
How can I pass a variable to called method on isolate?
How can I handling error on isolate?
First of all,
Isolate are not thread, they are independant process more like a fork() than a thread
dartApi: Isolate
Concurrent programming using isolates:
independent workers that are similar to threads but don't share memory, communicating only via
messages.
So, you can't access to the same variable than your parent process. It's a choice made by the dart team, because it's a mechanism usable when you compile your dart code in js. So it need to be possible in JS
How can I pass a variable to called method on isolate?
To do this, you need to see ReceivePort() like a unidirectionnal way of communication, so to pass variable in two way, you need two.
So on you main process:
pwConPort.listen((data) {
if (isolateSendPort == null && data is SendPort) {
isolateSendPort = data; // Receive the communication object of the isolate
isolateSendPort.send("Passsowr1222!");
} else {
print("Generated password: ${data}");
pwConPort.close();
}
}, onError: (err) {
print("SendPortError: ${err}");
});
});
In you isolate entry point :
sendPort.send(isolateConPort.sendPort);
isolateConPort.listen((data) {
// code ....
});
Note: be careful of what message you send. message send between one process and another need to respect some rules
DartApi: SendPort
The content of message can be: primitive values (null, num, bool,
double, String), instances of SendPort, and lists and maps whose
elements are any of these. List and maps are also allowed to be
cyclic.
How can I handling error on isolate?
Isolate get one method to listen throw error send by the isolate : addErrorListner
That is a useful function.
BUT ! this method is not implement in every plate-forme, so you need to do this in a others.
The way i chose is to send 2 SendPort in the entry point function :
One for the communication
One for the error.
So the spawn function looks like :
Isolate.spawn(generatePasswordConcurrency, [pwConPort.sendPort, errorPort.sendPort])
and the generatePasswordConcurrency :
void generatePasswordConcurrency(List<SendPort> commList) {
var sendPort = commList[0];
var errorPort = commList[1];
var isolateConPort = new ReceivePort();
sendPort.send(isolateConPort.sendPort);
isolateConPort.listen((data) {
try {
sendPort.send(_generateHashPassword(data));
} catch (e) {
errorPort.send("error: ${e.toString()}");
}
});
}
Here the full code :
import 'dart:isolate';
import 'package:dbcrypt/dbcrypt.dart';
main() {
var pwConPort = new ReceivePort();
var errorPort = new ReceivePort();
SendPort isolateSendPort = null;
Isolate.spawn(generatePasswordConcurrency, [pwConPort.sendPort, errorPort.sendPort])
.then((Isolate pcs) {
errorPort.listen((err) {
print("Error: ${err}");
pwConPort.close();
errorPort.close();
});
print(pcs);
pwConPort.listen((data) {
if (isolateSendPort == null && data is SendPort) {
isolateSendPort = data;
isolateSendPort.send("Passsowr1222!");
} else {
print("Generated password: ${data}");
pwConPort.close();
errorPort.close();
//pcs.kill();
}
}, onError: (err) {
print("SendPortError: ${err}");
});
});
}
void generatePasswordConcurrency(List<SendPort> commList) {
var sendPort = commList[0];
var errorPort = commList[1];
var isolateConPort = new ReceivePort();
sendPort.send(isolateConPort.sendPort);
isolateConPort.listen((data) {
try {
sendPort.send(_generateHashPassword(data));
} catch (e) {
errorPort.send("error: ${e.toString()}");
}
});
}
String _generateHashPassword(String password) {
var regex = new RegExp(r'^.*(?=.{7,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).*$');
if (!regex.hasMatch(password)) {
throw new StateError('Errors');
}
return new DBCrypt().hashpw(password, new DBCrypt().gensalt());
}
Related
Hi i am building a blockchain and am trying to sync a list of connected peers,
but if the following code is called twice from different nodes the first call is still busy while the second call kicks in does anyone know how i could wait for the first message to be complete
class Peer {
Peer(
{required this.us,
required this.peers,
required this.allPeers,
required this.myPeers});
String us;
Map<String, int> peers;
Map<String, List<Online>> allPeers;
Map<String, List<Online>> myPeers;
List<String>? keep;
Map<String, List<NewNodePeerMessage>> nnpms = {};
//listen should never trigger a response to connect we will give the address / ip
// so your ndoe two will only sync when a thrid node joins the network
Future listen() async {
ServerSocket ss =
await ServerSocket.bind(us.split(':')[0], int.parse(us.split(':')[1]));
print('listening on ${us.split(':')[1]}');
// List<ReceivePort> lrp = [];
ss.listen((client) {
// ReceivePort rp = ReceivePort();
utf8.decoder.bind(client).listen((data) async {
final PeerMessage pm =
PeerMessage.fromJson(json.decode(data) as Map<String, dynamic>);
print('recieved msg from ${pm.from}');
switch (pm.type) {
case 'new-node':
{
// rp.listen((_) async {
final NewNodePeerMessage nnpm = NewNodePeerMessage.fromJson(
json.decode(data) as Map<String, dynamic>);
print('msg${nnpm.toJson()}');
peers[pm.code] ??= 3;
allPeers[pm.code] ??= [];
final List<Online> news = [];
print('apl ${allPeers[pm.code]!.length}');
// ,maby a list would let me know
// maby we should have a simple check to the loop bool that it doesnt start looping when it is adjusting the peers
print(
'allpeers before looping ${allPeers[pm.code]!.map((e) => e.toJson()).toList()}');
for (Online one in allPeers[pm.code]!.where((element) =>
element.address != pm.from &&
element.address != us &&
!nnpm.recieved.contains(element.address))) {
print(
'i am still loopin current one ${one.toJson()} current from ${pm.from}');
try {
final Socket ones = await Socket.connect(
one.address.split(':')[0],
int.parse(one.address.split(':')[1]));
nnpm.recieved.add(us);
nnpm.recieved.add(pm.from);
// if we would only move ones out of the for loop maby the program wont work at the same point in time
// we could have a bool that keeeps track of the msg is working on printstatement you inside off the msg
// so 8787 trigger 5442 because it has him in the list
// mabe a bool can be added to list if you is inished with listening
// isbusy knows iff its stuck in the loop if we write from here we are
// if we write ffrom connect we arent or this write could know if it is busy
// is busy shoudl be from down
// because if we wirte from here or we write from you is busy is true and false
// so if we write from below can isbusy stop us
// is isusy is true herewe can go into listen but on you we can not
ones.write(json.encode(NewNodePeerMessage(
isBusy: false,
max: peers[pm.code]!,
peer: nnpm.peer,
type: 'new-node',
from: us,
code: pm.code,
recieved: nnpm.recieved)
.toJson()));
print('propablywrote ${one.toJson()} from ${nnpm.from}');
ones.listen((ppmru) async {
print('listentedtoppmru ${one.toJson()}');
final PeersPeerMessageResponse ppmr =
peersPeerMessageResponse(ppmru);
print(
'abouttoaddnewonlines ${ppmr.onlines.map((e) => e.toJson())} and from ${ppmr.from}');
// news.add(Online(online: true, address: ppmr.from));
news.addAll(ppmr.onlines);
await ones.close();
});
} catch (err) {
one.online = false;
// break;
}
print('abouttoloopagain ${one.toJson()} from ${nnpm.from}');
}
print('gothereagainactuallydonelooping ${pm.from}');
// its actually that new node only should write to the client again only if its the first time maby
allPeers[pm.code]!.addAll(news);
allPeers[pm.code]!.removeWhere((element) => !element.online);
print(allPeers[pm.code]!.map((e) => e.toJson()).toList());
/// the problem occurs because of client clients response will shut down base or we could wrap it inside o try an catch
/// we need to know if this message will send the code up here or down to printstatement you to ones.listen or to s that listen
/// one global bool could say like will go down maby even when it goes up herte to printstatement abouttoaddnewonlines
/// how do we know here below that it will go to the you printstatement or down
/// so we need a message from up
allPeers[pm.code]!.add(Online(online: true, address: nnpm.peer));
// print('abouttowriteto ${client.address.address} ${client.port}');
print('abouttorespondto ${pm.from}');
client.write(json.encode(PeersPeerMessageResponse(
isBusy: true,
peer: nnpm.peer,
onlines: allPeers[pm.code]!
.where((element) => element.address != pm.from)
.toList(),
code: pm.code,
from: us)
.toJson()));
// await client.close();
// rp.sendPort.send(null);
// });
client.destroy();
break;
}
case 'new-node-through':
{
break;
}
case 'is-online':
client.write(null);
break;
case 'is-test':
print('recieved');
client.write('irespond');
break;
default:
break;
}
// client.destroy();
}, onDone: () {});
});
}
bool loop = false;
// Future connect(List<dynamic> args) async {
// is busy shoudl bee ffrom down
Future connect(String bootnode, String code) async {
final Socket s = await Socket.connect(
"${bootnode.split(':')[0]}", int.parse(bootnode.split(':')[1]));
print('connected to ${bootnode}');
s.write(json.encode(NewNodePeerMessage(
isBusy: true,
max: 3,
peer: us,
type: 'new-node',
code: code,
from: us,
recieved: []).toJson()));
print('befforelistening');
s.listen((pmmru) async {
print('whatwas first');
PeersPeerMessageResponse ppmr = peersPeerMessageResponse(pmmru);
print(ppmr.toJson());
allPeers[ppmr.code] ??= [];
allPeers[ppmr.code]!.addAll(ppmr.onlines);
allPeers[ppmr.code]!.add(Online(online: true, address: ppmr.from));
print(allPeers);
await s.close();
}, onDone: () {
print('doschopnescheee');
});
//because off up being triggered ffrom this msg we know it will go down because off client that write
// and client that write is up their and it m
// final somekindloop;
//if we would just never listen here would it relay on up might solve problem because up might be busy we could also ssst the isolate
// s.listen((ppmru) async {
// loop = true;
// // whenever we recieve here the ppmru could have the isbusy instead
// // print('you');
// // isBusy
// // while (!loop) {}
// PeersPeerMessageResponse ppmr = peersPeerMessageResponse(ppmru);
// print(ppmr.toJson());
// allPeers[ppmr.code] ??= [];
// allPeers[ppmr.code]!.addAll(ppmr.onlines);
// allPeers[ppmr.code]!.add(Online(online: true, address: ppmr.from));
// print(allPeers);
// await s.close();
// });
}
PeersPeerMessageResponse peersPeerMessageResponse(Uint8List resp) {
final PeersPeerMessageResponse ppm = PeersPeerMessageResponse.fromJson(
json.decode(String.fromCharCodes(resp).trim()) as Map<String, dynamic>);
return ppm;
}
// Future isOnline(String code) async {
// for (Online p in allPeers[code] ??= []) {
// try {
// final Socket peer = await Socket.connect(
// p.address.split(':')[0], int.parse(p.address.split(':')[1]));
// await peer.close();
// } catch (err) {
// p.ischis = false;
// }
// }
// }
}
its about the new node function inside of the switch statement while the first call is inside off the for loop the second call does not loop but increments the allPeers with new peers which because off the first call evolves into
Unhandled exception:
Concurrent modification during iteration: Instance(length:3) of '_GrowableList'.
#0 ListIterator.moveNext (dart:_internal/iterable.dart:336:7)
#1 WhereIterator.moveNext (dart:_internal/iterable.dart:438:22)
#2 Peer.listen.<anonymous closure>.<anonymous closure> (package:gov/peer/peer.dart:180:53)
<asynchronous suspension>
how can is use the on done event to wait or the first call to be complete?
I am failing to understand, why the error thrown from addItem method in below code is not caught in the try-catch block
void main() async {
var executor = Executor();
var stream = Stream.fromIterable([0, 1, 2, 3, 4, 5, 6, 7]);
try {
await for (var _ in stream) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
Future<void> demoMethod() async {
var list = [1, 2, 3, 1, 4, 5];
var executor = Executor();
var test = Test();
for (var element in list) {
executor.submit(() => test.addItem(element));
}
await executor.execute();
test.list.forEach(print);
}
class Test {
var list = <int>[];
Future<void> addItem(int i) async {
if (list.contains(i)) {
throw Exception('Item exists');
}
list.add(i);
}
}
class Executor {
final List<Future<void>> _futures = [];
bool _disposed = false;
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation());
} else {
throw Exception('Executor is already disposed');
}
}
Future<void> execute() async {
await Future.wait(_futures, eagerError: true);
_disposed = true;
}
}
but below code is able to catch the error properly
void main() async {
var executor = Executor();
try {
for (var i = 0; i < 10; i++) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
I am guessing it has something to do with the stream processing.
It's the stream.
In your other examples, you synchronously run through a loop a and call Executor.submit with all the computations, then immediately call executor.execute().
There is no asychronous gap between calling the function which returns a future, and Future.wait starting to wait for that future.
In the stream code, each stream events starts an asynchronous computation by calling Executor.submit. That creates a future, stores it in a list, and goes back to waiting for the stream.
If that future completes, with an error, before the stream ends and Future.wait gets called, then there is no error handler attached to the future yet. The error is then considered unhandled, and is reported to the current Zone's uncaught error handler. Here that's the root zone, which means it's a global uncaught error, which may crash your entire program.
You need to make sure the future doesn't consider its error unhandled.
The easiest way to do that is to change submit to:
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation()..ignore());
} else {
throw StateError('Executor is already disposed');
}
}
The ..ignore() tells the future that it's OK to not have an error handler.
You know, because the code will later come back and call executor.execute, that any errors will still be reported, so it should be safe to just postpone them a little. That's what Future.ignore is for.
(Also changed Exception to StateError, because that's what you should use to report people using objects that have been disposed or otherwise decommissioned.)
After reading a bunch of documentation about Streams and StreamControllers in dart I tried to build a little example and was surprised of the results. All documentation I have read states that a stream starts emiting data as soon as a listener is registered. But this doesn't show any printed data:
class Order
{
String type;
Order(this.type);
}
class Pizza
{
}
void main()
{
Order order = Order("pzza");
final StreamController sc = StreamController();
sc.sink.add(order);
sc.sink.add(order);
sc.sink.add(new Order("pizza"));
Stream st = sc.stream.map((order) {
return order.type;
})
.map((orderType) {
if(orderType == "pizza")
return Pizza();
else
throw ("dude!, I don't know how to do that");
});
var sus = st.listen((pizza)
{
print("We did a pizza");
},
onError: (error)
{
print(error);
});
sus.cancel();
sc.sink.add(new Order("pizza2"));
}
I was expecting this output:
dude!, I don't know how to do that
dude!, I don't know how to do that
We did a pizza
When creating streams and adding data is all "sinked" data scheduled to be emited on the next application step?
Cheers.
You are right in that the documentation states that you listen on a stream to make it start generating events. However, streams are asynchronous so when you call the listen() method you are registering to receive events from the stream at some point in the future. Dart will then continue to run the remainder of your main function. Immediately after calling listen() you call cancel() to cancel the subscription which is why nothing is being printed.
If you remove or comment out the cancel and run it again you will see the expected output.
A slightly modified version of your code will hopefully highlight the run of events:
class Order {
String type;
Order(this.type);
}
class Pizza {}
void main() {
print("Main starts");
Order order = Order("pzza");
final StreamController sc = StreamController();
sc.sink.add(order);
sc.sink.add(order);
sc.sink.add(new Order("pizza"));
Stream st = sc.stream.map((order) {
return order.type;
}).map((orderType) {
if (orderType == "pizza")
return Pizza();
else
throw ("dude!, I don't know how to do that");
});
var sus = st.listen((pizza) {
print("We did a pizza");
}, onError: (error) {
print(error);
});
// sus.cancel();
sc.sink.add(new Order("pizza2"));
print("Main ends");
}
Running this produces the output:
Main starts
Main ends
dude!, I don't know how to do that
dude!, I don't know how to do that
We did a pizza
dude!, I don't know how to do that
After many attempts to get the content of the response in HttpRequest, I failed completely to know or understand why I can't have what I want, and I must mention that I can log and manipulate the response only inside an onReadyStateChange (onLoad and onLoadEnd are giving me the same results!), but I really want that value outside the callback.
Here is the part of code that I'm stuck with
Map responsData;
req=new HttpRequest()
..open(method,url)
..send(infojson);
req.onReadyStateChange.listen((ProgressEvent e){
if (req.readyState == HttpRequest.DONE ){
if(req.status == 200){
responsData = {'data': req.responseText};
print("data receaved: ${ req.responseText}");
//will log {"data":mydata}
}
if(req.status == 0){
responsData = {'data':'No server'};
print(responsData );
//will log {"data":No server}
}
}
});
//anything here to get responsData won't work
You have to assign an onLoad callback before you call send.
I'm not sure what you mean with only inside an onReadyStateChange.
Maybe you want to assign the responseText to a variable outside the the callback.
Create a method:
Future<String> send(String method, String url, String infojson) {
var completer = new Completer<String>();
// var result;
req=new HttpRequest()
..open(method,url)
..onLoad.listen((event) {
//print('Request complete ${event.target.reponseText}'))
// result = event.target.responseText;
completer.complete(event.target.responseText);
})
..send(infojson);
return completer.future;
}
and call this method like
var result;
send(method, url).then(
(e) {
// result = e;
print('Request complete ${e}'));
});
(I'm using the new lib v2 version of dart:io.)
I'd like to register multiple handlers for an HttpServer, specifically a WebSocketTransformer and other arbitrary handlers. Something like this:
// pseudo-code
var server = HttpServer;
server.register('/foo', someHandlerFunction); // 1
server.register('/bar', someOtherHandlerFunction); // 2
server.register('/ws', webSocketHandler); // 3
If #1 matches, then #2 isn't tested, and so on. So, it's greedy.
I've seen samples with just one handler. How do I register many handlers? Thanks in advance!
New answer: Use the route package: http://pub.dartlang.org/packages/route
Here's your example using route's serve() method:
HttpServer.bind('127.0.0.1', 8889).then((server) {
var router = new Router(server)
..serve('/ws').transform(new WebSocketTransformer()).listen(handleWebSocket)
..serve('/foo').listen((req) {
req.response..addString('foo')..close();
});
});
Router automatically catches unhandled requests and sends a 404, though soon you'll be able to override that with a defaultStream you can listen to.
Router also supports filters, useful for logging, auth, compression, etc.:
HttpServer.bind('127.0.0.1', 8889).then((server) {
var router = new Router(server)
..filter(new RegExp(r'/.*'), (req) {
//log all requests
_logger.info("request: $req");
return new Future.immediate(true); // keep processing request
})
..filter(new Regexp(r'/secure/.*'), (req) {
// check authentication asynchronously
return getUserFromRequest(req).then((user) {
if (user == null) {
sendRedirect('/login'); // sendRedirect coming soon
return false; // stop processing request
} else {
return true; // keep processing
}
});
})
..serve(/* ... */ );
});
Here's how the API docs recommend to register a WebSocket handler:
server
.where((request) => request.uri.path == "/ws")
.transform(new WebSocketTransformer()).listen((webSocket) => ...);
However, the server is a single-subscription stream. Once a listen is attached, you can't attach other listeners.
What I really want is for something to look at an event, decide if it can handle it, and if so then route it to another stream. Otherwise, pass it along. This way, the event (in this case an HttpRequest object) is passed along a chain until it's handled.
I built a TakeAndRoute class that extends StreamEventTransformer. The TakeAndRoute uses a function to determine if it should grab the event and route it to another stream, or simply forward it along.
Here's what I came up with:
import 'dart:io';
import 'dart:async';
handleWebSocket(WebSocket webSocket) {
webSocket.listen((event) {
if (event is MessageEvent) {
/* Handle message. */
} else if (event is CloseEvent) {
/* Handle closed. */
}
});
}
typedef bool ShouldTake(e);
typedef void RouteTo(Stream stream);
typedef void HandleEvent(e);
class TakeAndRoute<S, T> extends StreamEventTransformer<S, T> {
ShouldTake shouldTake;
RouteTo routeTo;
StreamController controller = new StreamController();
HandleEvent handler;
TakeAndRoute(this.shouldTake, {this.routeTo, this.handler}) {
if (routeTo != null) routeTo(controller.stream);
}
handleData(event, StreamSink sink) {
print("handling");
if (shouldTake(event)) {
if (routeTo != null) {
controller.add(event);
}
if (handler != null) {
handler(event);
}
} else {
sink.add(event);
}
}
}
main() {
HttpServer.bind('127.0.0.1', 8888)
.then((HttpServer server) {
server
.transform(new TakeAndRoute<HttpRequest, HttpRequest>(
(req) => req.uri.path == '/ws',
routeTo: (stream) => stream.transform(new WebSocketTransformer()).listen(handleWebSocket)))
.transform(new TakeAndRoute<HttpRequest, HttpRequest>(
(req) => req.uri.path == '/foo',
handler: (req) {
print('got foo');
req.response.addString("foo");
req.response.close();
}))
.listen((req) {
print("got 404 for ${req.uri}");
req.response.statusCode = 404;
req.response.close();
});
});
}
Admittedly, this might be overkill.
Here is a more manual, but shorter way to do it:
HttpServer.bind('127.0.0.1', 8889)
.then((HttpServer server) {
var sc = new StreamController();
sc.stream.transform(new WebSocketTransformer()).listen(handleWebSocket);
server.listen((HttpRequest request) {
print("new connection from ${request.uri.scheme} ${request.uri}");
// See https://code.google.com/p/dart/issues/detail?id=8825
//if (request.uri.scheme == 'ws') {
if (request.uri.path == '/ws') {
sc.add(request);
} else if (request.uri.path == '/foo') {
request.response.addString('foo');
request.response.close();
} else {
print("got 404 for ${request.uri}");
request.response.statusCode = 404;
request.response.close();
}
});
});
Notice how I had to create a StreamController so I could pump events to WebSocketTransformer