async.Future async.Completer - how to "continue" if an error - dart

Some help with the following would be appreciated. I am writing some console test programs, and I want to be able to enter some parameters from the terminal (I don't want to use command line arguments - too many parameters). I have tried some variations, but I cannot find how to accomplish this. The following is the latest version of my test for terminal input. The problem with this program is that if an error is encountered, the Completer closes automatically, and I want to continue from either the Main() or from fGetNumber() function. While I can see why this program doesn't work, it illustrates what I need to achieve - re-enter the number, but I cannot find how to achieve that. If a valid number is entered, there is no problem. If an invalid number is entered, I cannot find out how to re-enter the number.
The code is as follows, and the problem I have is highlighted by "//////////" :
import "dart:async" as async;
import "dart:io";
void main() {
fGetNumber("Enter Nr of Iterations : ", 0, 999999)
.then((int iIters){
print ("In Main : Iterations selected = ${iIters}");
if (iIters == null) {
print ("In Main: Invalid Number of iterations : ${iIters}.");
} else {
fProcessData(iIters);
}
print ("Main Completed");
});
}
async.Future<int> fGetNumber(String sPrompt, int iMin, int iMax) {
print ("In fGetNumber");
int iIters = 0;
async.Completer<int> oCompleter = new async.Completer();
while (!oCompleter.isCompleted) { /////////// This loop does not work ///////
return fGetUserInput(sPrompt).then((String sIters) {
iIters = int.parse(sIters);
if (iIters < iMin || iIters > iMax) throw new Exception("Invalid");
oCompleter.complete(iIters);
return oCompleter.future;
}).catchError((_) => print ("Invalid - number must be from ${iMin} to ${iMax}")
).whenComplete(() => print ("fGetNumber - whenComplete"));// always gets here
}
print ("In fGetNumber (at end of function)"); //// it never gets here
}
async.Future<String> fGetUserInput(String sPrompt) {
print ("In fGetUserInput");
async.Completer<String> oCompleter = new async.Completer();
stdout.write(sPrompt);
async.Stream<String> oStream = stdin.transform(new StringDecoder());
async.StreamSubscription oSub;
oSub = oStream.listen((String sData) {
oCompleter.complete("$sData");
oSub.cancel();
});
return oCompleter.future;
}
void fProcessData(int iIters) {
print ("In fProcessData");
for (int iPos = 1; iPos <= iIters; iPos++ ) {
if (iPos%100 == 0) print ("Processed = ${iPos}");
}
print ("In fProcessData - completed ${iIters}");
}

// This loop does not work
Of course it does - you enter it exactly once, where you immediately return and therefore leave the loop and method.
// always gets here
That's because whenComplete() always gets called, on success or on error.
// it never gets here
Because you already returned out of the method.
So what can be done?
The easiest way would be to not rely on fGetUserInput(). Listen to stdin in fGetNumber and only complete the completer / cancel the subscription if the input is valid:
async.Future<int> fGetNumber(String sPrompt, int iMin, int iMax) {
print ("In fGetNumber");
async.Completer<String> oCompleter = new async.Completer();
stdout.write(sPrompt);
async.Stream<String> oStream = stdin.transform(new StringDecoder());
async.StreamSubscription oSub;
oSub = oStream.listen((String sData) {
try {
int iIters = int.parse(sData);
if (iIters < iMin || iIters > iMax) throw new Exception("Invalid");
oCompleter.complete(iIters);
oSub.cancel();
} catch(e) {
print("Invalid - number must be from ${iMin} to ${iMax}");
stdout.write(sPrompt);
}
});
return oCompleter.future;
}
Are there alternatives?
Of course. There are likely many, many ways to do this. This one for example:
async.Future<int> fGetNumber(String sPrompt, int iMin, int iMax) {
print ("In fGetNumber");
async.Completer<int> oCompleter = new async.Completer();
fGetUserInput(sPrompt, oCompleter, (String sIters) {
try {
int iIters = int.parse(sIters);
if (iIters < iMin || iIters > iMax) throw new Exception("Invalid");
return iIters;
} catch(e) {
print ("Invalid - number must be from ${iMin} to ${iMax}");
stdout.write(sPrompt);
}
return null;
});
return oCompleter.future;
}
void fGetUserInput(String sPrompt, async.Completer oCompleter, dynamic inputValidator(String sData)) {
print ("In fGetUserInput");
stdout.write(sPrompt);
async.Stream<String> oStream = stdin.transform(new StringDecoder());
async.StreamSubscription oSub;
oSub = oStream.listen((String sData) {
var d = inputValidator(sData);
if(d != null) {
oCompleter.complete(d);
oSub.cancel();
}
});
}
If you really feel there should be something addressed by the Dart team, you could write a feature request. But the Completer is designed to only be completed once. Whatever code you write, you can't just loop to complete it again and again.

Related

Why does assignment not work the same in Dart as it does in Python?

When I run this code:
void readCard(db, [int card_id = -1]) {
if (card_id == -1) {
final ResultSet result = db.select('SELECT * FROM cards');
}
else {
final ResultSet result = db.select("""
SELECT * FROM cards
WHERE card_id=(?)
"""); // this doesn't work yet
}
for (final Row card in result) {
print(
"Card {'card_id': ${card['card_id']}, "
"'due': ${card['due']}, "
"'content': ${card['content']}}"
);
}
}
I get this error:
memotext.dart:66:25: Error: Getter not found: 'result'.
for (final Row card in result) {
^^^^^^
Because result is assigned regardless of whether the if statement or the the else statement runs shouldn't there be no error? Is this something to do with the way dart does assignment?
The result is declared in their respective blocks, it doesn't exist outside. Do this:
void readCard(db, [int card_id = -1]) {
ResultSet result;
if (card_id == -1) {
result = db.select('SELECT * FROM cards');
}
else {
result = db.select("""
SELECT * FROM cards
WHERE card_id=(?)
"""); // this doesn't work yet
}
for (final Row card in result) {
print(
"Card {'card_id': ${card['card_id']}, "
"'due': ${card['due']}, "
"'content': ${card['content']}}"
);
}
}

return List of Strings from Future / Stream

My Problem is very similar to the one mentioned here and here, but for some reason these are not working for me.
Basically, I want to do some simple I/O operations (on a mobile), returning of list of strings (folder path) that contain a certain file format (let's assume for the sake of argument that I want to find all mp3 files).
This is the code I have
Future<List<String>> getFolders() async {
List<String> _dirs = new List();
await SimplePermissions.requestPermission(Permission.ReadExternalStorage);
_dirs = await findAllFolders();
return _dirs;
}
Future<List<String>> findAllFolders() async {
Directory _root = Directory("/sdcard");
bool _notInList = true;
List<String> _dirs = new List();
_root.list(recursive: true, followLinks: false)
.listen((FileSystemEntity entity) {
if(entity.toString().contains("mp3")) {
if(_dirs.length==0) {
_dirs.add(entity.parent.path.toString());
} else {
_notInList = true;
for (var i = 0; i < _dirs.length; ++i) {
if(_dirs[i] == entity.parent.path.toString()) {
_notInList = false;
}
}
if(_notInList) {
_dirs.add(entity.parent.path.toString());
}
}
}
});
return _dirs;
}
where I want to use _dirs outside of getFolders().
I know that findAllFolders() returns _dirs immediately, before my listen() event has finished (and so its length is always 0, although the actual method works fine, i.e. if I put print statements where I have _dirs.add() I can see that the correct directories are added, _dirs contains what I want but I have no idea how to return the finished _dirs list). I tried to do something in a similar way to the above mentioned post, where a Completer is used (to which I am getting an error message "Bad State: Future already completed"). The respective code would be
Future<List<String>> findAllFolders() async {
Directory _root = Directory("/sdcard");
bool _notInList = true;
List<String> _dirs = new List();
Completer<List<String>> _completer = new Completer<List<String>>();
_root.list(recursive: true, followLinks: false)
.listen((FileSystemEntity entity) {
if(entity.toString().contains("mp3")) {
if(_dirs.length==0) {
_dirs.add(entity.parent.path.toString());
} else {
_notInList = true;
for (var i = 0; i < _dirs.length; ++i) {
if(_dirs[i] == entity.parent.path.toString()) {
_notInList = false;
}
}
if(_notInList) {
_dirs.add(entity.parent.path.toString());
}
}
}
_completer.complete(_dirs);
});
return _completer.future;
}
The getFolders() function remains the same in this case. Could anyone point out where my logic is going wrong?
You're setting a listener, then immediately returning before any results are received - that's why your return is always empty. The body of findAllFolders() needs to wait for a response before returning. Try the below to replace _root.list().listen():
List<FileSystemEntity> files = await _root.list(recursive: true, followLinks: false).toList();
for (FileSystemEntity entity in files) {
// Do your filename logic and populate _dirs

tryparse a value form a file

what i am trying to do is take the variable from the file but throw an exception if input is not a number. i just want an error message to show when the entered amount is a word or negative number. i want to use a try catch but am not sure how to structure it. thanks you guys.
StreamReader read = new StreamReader("../../data.dat");
Stopwatch st = new Stopwatch();
bool ok;
int num;
string input=(read.ReadLine());
ok = int.TryParse(input, out num);
if (ok ==false)
{
throw new Exception("Input in incorrect format");
}
int sum = 0;
Scanner scan = new Scanner("../../data.dat");
int num = Integer.MIN_VALUE;
try {
num = Integer.parseInt(scan.next());
} catch (Exception e) {
System.out.println("Input in incorrect format.");
e.printStackTrace();
}
scan.close();

async Future StreamSubscription Error

Could someone please explain what's wrong with the following code. I'm making two calls to the function fInputData. The first works ok, the second results in an error :
"unhandled exception"
"Bad state: Stream already has subscriber"
I need to write a test console program that inputs multiple parameters.
import "dart:async" as async;
import "dart:io";
void main() {
fInputData ("Enter Nr of Iterations : ")
.then((String sResult){
int iIters;
try {
iIters = int.parse(sResult);
if (iIters < 0) throw new Exception("Invalid");
} catch (oError) {
print ("Invalid entry");
exit(1);
}
print ("In Main : Iterations selected = ${iIters}");
fInputData("Continue Processing? (Y/N) : ") // this call bombs
.then((String sInput){
if (sInput != "y" && sInput != "Y")
exit(1);
fProcessData(iIters);
print ("Main Completed");
});
});
}
async.Future<String> fInputData(String sPrompt) {
async.Completer<String> oCompleter = new async.Completer();
stdout.write(sPrompt);
async.Stream<String> oStream = stdin.transform(new StringDecoder());
async.StreamSubscription oSub;
oSub = oStream.listen((String sInput) {
oCompleter.complete(sInput);
oSub.cancel();
});
return oCompleter.future;
}
void fProcessData(int iIters) {
print ("In fProcessData");
print ("iIters = ${iIters}");
for (int iPos = 1; iPos <= iIters; iPos++ ) {
if (iPos%100 == 0) print ("Processed = ${iPos}");
}
print ("In fProcessData - completed ${iIters}");
}
Some background reading:
Streams comes in two flavours: single or multiple (also known as
broadcast) subscriber. By default, our stream is a single-subscriber
stream. This means that if you try to listen to the stream more than
once, you will get an exception, and using any of the callback
functions or future properties counts as listening.
You can convert the single-subscriber stream into a broadcast stream
by using the asBroadcastStream() method.
So you've got two options - either re-use a single subscription object. i.e. call listen once, and keep the subscription object alive.
Or use a broadcast stream - note there are a number of differences between broadcast streams and single-subscriber streams, you'll need to read about those and make sure they suit your use-case.
Here's an example of reusing a subscriber to ask multiple questions:
import 'dart:async';
import 'dart:io';
main() {
var console = new Console();
var loop;
loop = () => ask(console).then((_) => loop());
loop();
}
Future ask(Console console) {
print('1 + 1 = ...');
return console.readLine().then((line) {
print(line.trim() == '2' ? 'Yup!' : 'Nope :(');
});
}
class Console {
StreamSubscription<String> _subs;
Console() {
var input = stdin
.transform(new StringDecoder())
.transform(new LineTransformer());
_subs = input.listen(null);
}
Future<String> readLine() {
var completer = new Completer<String>();
_subs.onData(completer.complete);
return completer.future;
}
}

Unable to acquire a mutex held by a ACE_Condition wait

I have the following code which is used to Push and Pend from a queue. The caller code has multiple MsgQ objects. It is possible that the Push and the Pend functions are waiting on the _notFull->wait() and the _notEmpty->wait() conditional waits. These waits are protected by the _mut mutex. The notFull and the notEmpty waits operate on the empty and full variables.
When the destructor is called, the _deleteQueue is called internally, from which I would like to signal to the waiting threads to cleanup and stop waiting for a signal to come. Once that is done, I delete my objects. However, in the _deleteQueue function, when I attempt to do _mut->acquire(), I am unable to acquire the mutex. Even if I ignore the acquire, I am unable to broadcast to these waiting threads. Where am I going wrong?
Thanks,
Vikram.
MsgQ::~MsgQ()
{
_deleteQueue();
delete _mut;_mut=NULL;
delete _notFull;_notFull=NULL;
delete _notEmpty;_notEmpty=NULL;
delete _PostMutex; _PostMutex = NULL;
delete _PendMutex; _PendMutex = NULL;
delete _PostInProgressMutex; _PostInProgressMutex = NULL;
delete _PendInProgressMutex; _PendInProgressMutex = NULL;
delete _DisconnectMutex; _DisconnectMutex = NULL;
free( _ptrQueue ); _ptrQueue = NULL;
}
int MsgQ::Post(Message* msg)
{
_PostMutex->acquire();
_postInProgress++;
_PostMutex->release();
if (msg)
msg->print();
_mut->acquire();
while (full)
{
_notFull->wait();
}
if (!_disconnectInProgress)
_queuePush(msg);
_mut->release();
_PostMutex->acquire();
_postInProgress--;
if (_postInProgress==0)
{
_PostInProgressMutex->signal();
}
_PostMutex->release();
return _notEmpty->signal();
}
int MsgQ::Pend(Message*& msg)
{
_PendMutex->acquire();
_pendInProgress++;
_PendMutex->release();
_mut->acquire();
while (empty)
_notEmpty->wait();
if (!_disconnectInProgress)
{
_queuePop(msg);
}
_mut->release();
_PendMutex->acquire();
_pendInProgress--;
if (_pendInProgress == 0)
{
_PendInProgressMutex->signal();
}
_PendMutex->release();
return _notFull->signal();
}
void MsgQ::_deleteQueue ()
{
_PostMutex->acquire();
if (_postInProgress != 0)
{
_PostMutex->release();
TRACE("Acquiring Mutex.");
_mut->acquire();
full = 0;
_notFull->broadcast();
_mut->release();
_PostInProgressMutex->wait();
}
else
{
_PostMutex->release();
}
_PendMutex->acquire();
if (_pendInProgress != 0)
{
_PendMutex->release();
TRACE("Acquiring Mutex.");
_mut->acquire();
empty = 0;
_notEmpty->broadcast();
_mut->release();
_PendInProgressMutex->wait();
}
else
{
_PendMutex->release();
}
}
void MsgQ::_initQueue()
{
_ptrQueue = (Message **)(malloc (size * sizeof (Message*)));
if (_ptrQueue == NULL)
{
cout << "queue could not be created!" << endl;
}
else
{
for (int i = 0; i < size; i++)
*(_ptrQueue + i) = NULL;
empty = 1;
full = 0;
head = 0;
tail = 0;
try{
_mut = new ACE_Mutex() ;
_notFull = new ACE_Condition<ACE_Mutex>(*_mut);
_notEmpty = new ACE_Condition<ACE_Mutex>(*_mut);
_PostMutex = new ACE_Mutex();
_PendMutex = new ACE_Mutex();
_PostInProgressMutex = new ACE_Condition<ACE_Mutex>(*_PostMutex);
_PendInProgressMutex = new ACE_Condition<ACE_Mutex>(*_PendMutex);
_DisconnectMutex = new ACE_Mutex();
_postInProgress = 0;
_pendInProgress = 0;
_disconnectInProgress = false;
}catch(...){
cout << "you should not be here" << endl;
}
}
}
There seem to be many problems with the code so I would suggest reworking it:
You have a potential for deadlock because you are acquiring _mut before you go into a wait condition in both Post and Pend functions.
Instead of using acquire and release on a ACE_Mutex I would suggest looking at using ACE_Guard class which can acquire mutex when created and release it when destroyed.
Why not use ACE_Message_Queue instead of creating your own?

Resources