I'm trying to implement chunk response in webapp using PLay 2 with Akka. However, instead of load the response by chunk by chunk all the response is coming as once. Below is the code by which I'm creating chunk in the controller:
/**
*
*/
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import org.pmw.tinylog.Logger;
import play.cache.CacheApi;
import play.cache.Cached;
import play.filters.csrf.AddCSRFToken;
import play.filters.csrf.CSRF;
import play.libs.Json;
import play.libs.concurrent.HttpExecutionContext;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Http.Cookie;
import play.mvc.Result;
import akka.NotUsed;
import akka.actor.Status;
import akka.stream.OverflowStrategy;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
/**
* #author Abhinabyte
*
*/
#Singleton
#AddCSRFToken
public class GetHandler extends Controller {
#Inject
private CacheApi cache;
#Inject
private HttpExecutionContext httpExecutionContext;
public CompletionStage<Result> index() {
return CompletableFuture.supplyAsync( () ->
Source.<ByteString>actorRef(256, OverflowStrategy.dropNew())
.mapMaterializedValue(sourceActor -> {
CompletableFuture.runAsync(() -> {
sourceActor.tell(ByteString.fromString("1"), null);
sourceActor.tell(ByteString.fromString("2"), null);
sourceActor.tell(ByteString.fromString("3"), null);
try {
Thread.sleep(3000);//intentional delay
} catch (InterruptedException e) {
e.printStackTrace();
}
sourceActor.tell(ByteString.fromString("444444444444444444444444444444444444444444444444444444444444444444444444"), null);
sourceActor.tell(new Status.Success(NotUsed.getInstance()), null);
});
return sourceActor;
})
).thenApplyAsync( chunks -> ok().chunked(chunks).as("text/html"));
}
}
And below is the Akka thread pool configuration at application.conf :
akka {
jvm-exit-on-fatal-error = on
actor {
default-dispatcher {
fork-join-executor {
parallelism-factor = 1.0
parallelism-max = 64
task-peeking-mode = LIFO
}
}
}
}
play.server.netty {
eventLoopThreads = 0
maxInitialLineLength = 4096
log.wire = false
transport = "native"
}
As you can see before sending last to last chunk I'm intentionally delaying the response time. So logically, all chunked data before it should be delivered before it.
However, in my case whole bunch of data is getting loaded. I've tested in all browser(even have tried to CURL).
What I'm missing in here?
Blocking in mapMaterializedValue will do that because it runs in the Akka default-dispatcher thread, thus preventing message routing for the duration (see this answer for details). You want to dispatch your slow, blocking code asynchronously, with the actor reference for it to post messages to. Your example will do what you expect if you run it in a future:
public CompletionStage<Result> test() {
return CompletableFuture.supplyAsync( () ->
Source.<ByteString>actorRef(256, OverflowStrategy.dropNew())
.mapMaterializedValue(sourceActor -> {
CompletableFuture.runAsync(() -> {
for (int i = 0; i < 20; i++) {
sourceActor.tell(ByteString.fromString(String.valueOf(i) + "<br/>\n"), null);
try {
Thread.sleep(500);//intentional delay
} catch (InterruptedException e) {
e.printStackTrace();
}
}
sourceActor.tell(new Status.Success(NotUsed.getInstance()), null);
});
return sourceActor;
})
).thenApplyAsync( chunks -> ok().chunked(chunks).as("text/html"));
}
If you check the Source code, you can see that the first parameter is bufferSize
public static <T> Source<T,ActorRef> actorRef(int bufferSize,
OverflowStrategy overflowStrategy)
all your elements that you generate in the stream probably have less then 256 bytes, hence only one http chunk is generated. Try to add more elements like in #Mikesname example.
It might me useful, if you need chunked response by using other approach.
public Result test() {
try {
// Finite list
List<String> sourceList = Arrays.asList("kiki", "foo", "bar");
Source<String, ?> source = Source.from(sourceList);
/* Following DB call, which fetch a record at a time, and send it as chunked response.
final Iterator<String> sourceIterator = Person.fetchAll();
Source<String, ?> source = Source.from(() -> sourceIterator); */
return ok().chunked(source.via(Flow.of(String.class).map(ByteString::fromString))).as(Http.MimeTypes.TEXT);
} catch (Exception e) {
return badRequest(e.getMessage());
}
}
Related
I have been trying to change the projection of Shapefile from one coordinate reference system to other. The shapefile I have used has EPSG:4326 as its reference system and I need to change it to EPSG:32056.
I am using Geotools API-20.0 for the same.
I have already tried various methods available in the geotools like using ReprojectingFeatureCollection, use of JTS, use of Query API to convert the shapefile directly to the other coordinate reference system
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JToolBar;
import javax.swing.SwingWorker;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureWriter;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.CRS;
import org.geotools.referencing.ReferencingFactoryFinder;
import org.geotools.referencing.factory.gridshift.GridShiftLocator;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.JProgressWindow;
import org.geotools.swing.action.SafeAction;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.Envelope;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureVisitor;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.util.ProgressListener;
import com.vividsolutions.jts.geom.Geometry;
public class CRSLab {
private File sourceFile;
private SimpleFeatureSource featureSource;
private MapContent map;
public static void main(String[] args) throws Exception {
CRSLab lab = new CRSLab();
lab.displayShapefile();
}
// docs end main
/**
* This method:
* <ol type="1">
* <li>Prompts the user for a shapefile to display
* <li>Creates a JMapFrame with custom toolbar buttons
* <li>Displays the shapefile
* </ol>
*/
// docs start display
private void displayShapefile() throws Exception {
sourceFile = JFileDataStoreChooser.showOpenFile("shp", null);
if (sourceFile == null) {
return;
}
FileDataStore store = FileDataStoreFinder.getDataStore(sourceFile);
featureSource = store.getFeatureSource();
// Create a map context and add our shapefile to it
map = new MapContent();
Style style = SLD.createSimpleStyle(featureSource.getSchema());
Layer layer = new FeatureLayer(featureSource, style);
map.layers().add(layer);
// Create a JMapFrame with custom toolbar buttons
JMapFrame mapFrame = new JMapFrame(map);
mapFrame.enableToolBar(true);
mapFrame.enableStatusBar(true);
JToolBar toolbar = mapFrame.getToolBar();
toolbar.addSeparator();
toolbar.add(new JButton(new ValidateGeometryAction()));
toolbar.add(new JButton(new ExportShapefileAction()));
// Display the map frame. When it is closed the application will exit
mapFrame.setSize(800, 600);
mapFrame.setVisible(true);
}
// docs end display
// docs start export
private void exportToShapefile() throws Exception {
SimpleFeatureType schema = featureSource.getSchema();
JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
chooser.setDialogTitle("Save reprojected shapefile");
chooser.setSaveFile(sourceFile);
int returnVal = chooser.showSaveDialog(null);
if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
return;
}
File file = chooser.getSelectedFile();
if (file.equals(sourceFile)) {
JOptionPane.showMessageDialog(null, "Cannot replace " + file);
return;
}
// set up the math transform used to process the data
CoordinateReferenceSystem dataCRS = schema.getCoordinateReferenceSystem();
CoordinateReferenceSystem worldCRS = CRS.decode("EPSG:32056", true);// map.getCoordinateReferenceSystem();
boolean lenient = true; // allow for some error due to different datums
MathTransform transform = CRS.findMathTransform(dataCRS, worldCRS, lenient);
// grab all features
SimpleFeatureCollection featureCollection = featureSource.getFeatures();
// And create a new Shapefile with a slight modified schema
DataStoreFactorySpi factory = new ShapefileDataStoreFactory();
Map<String, Serializable> create = new HashMap<String, Serializable>();
create.put("url", file.toURI().toURL());
create.put("create spatial index", Boolean.TRUE);
DataStore dataStore = factory.createNewDataStore(create);
SimpleFeatureType featureType = SimpleFeatureTypeBuilder.retype(schema, worldCRS);
dataStore.createSchema(featureType);
String createdName = dataStore.getTypeNames()[0];
// carefully open an iterator and writer to process the results
Transaction transaction = new DefaultTransaction("Reproject");
FeatureWriter<SimpleFeatureType, SimpleFeature> writer = dataStore.getFeatureWriterAppend(createdName,
transaction);
SimpleFeatureIterator iterator = featureCollection.features();
try {
int counter = 0;
while (iterator.hasNext()) {
// copy the contents of each feature and transform the geometry
SimpleFeature feature = iterator.next();
SimpleFeature copy = writer.next();
org.locationtech.jts.geom.Geometry geometry = (org.locationtech.jts.geom.Geometry) feature
.getDefaultGeometry();
org.locationtech.jts.geom.Geometry geometry2 = JTS.transform(geometry, transform);
System.out.println(geometry.isSimple() && geometry2.isSimple());
// if (geometry2.isValid()) {
copy.setAttributes(feature.getAttributes());
counter++;
copy.setDefaultGeometry(geometry2);
writer.write();
// }
}
transaction.commit();
System.out.println("valid geometries : " + counter);
JOptionPane.showMessageDialog(null, "Export to shapefile complete");
} catch (Exception problem) {
problem.printStackTrace();
transaction.rollback();
JOptionPane.showMessageDialog(null, "Export to shapefile failed");
} finally {
writer.close();
iterator.close();
transaction.close();
}
}
// docs end export
// docs start validate
private int validateFeatureGeometry(ProgressListener progress) throws Exception {
final SimpleFeatureCollection featureCollection = featureSource.getFeatures();
// Rather than use an iterator, create a FeatureVisitor to check each
// fature
class ValidationVisitor implements FeatureVisitor {
public int numInvalidGeometries = 0;
public void visit(Feature f) {
SimpleFeature feature = (SimpleFeature) f;
Geometry geom = (Geometry) feature.getDefaultGeometry();
if (geom != null && !geom.isValid()) {
numInvalidGeometries++;
System.out.println("Invalid Geoemtry: " + feature.getID());
}
}
}
ValidationVisitor visitor = new ValidationVisitor();
// Pass visitor and the progress bar to feature collection
featureCollection.accepts(visitor, progress);
return visitor.numInvalidGeometries;
}
// docs end validate
// docs start export action
class ExportShapefileAction extends SafeAction {
ExportShapefileAction() {
super("Export...");
putValue(Action.SHORT_DESCRIPTION, "Export using current crs");
}
public void action(ActionEvent e) throws Throwable {
exportToShapefile();
}
}
// docs end export action
/**
* This class performs the task of checking that the Geometry of each
* feature is topologically valid and reports on the results. It also
* supplies the name and tool tip.
*/
// docs start validate action
class ValidateGeometryAction extends SafeAction {
ValidateGeometryAction() {
super("Validate geometry");
putValue(Action.SHORT_DESCRIPTION, "Check each geometry");
}
public void action(ActionEvent e) throws Throwable {
int numInvalid = validateFeatureGeometry(null);
String msg;
if (numInvalid == 0) {
msg = "All feature geometries are valid";
} else {
msg = "Invalid geometries: " + numInvalid;
}
JOptionPane.showMessageDialog(null, msg, "Geometry results", JOptionPane.INFORMATION_MESSAGE);
}
}
// docs end validate action
}
The output obtained after doing projection using Geotools are a lot different than what I used to get from ArcMap of esri. Is there any other transformation that I should perform.
When I try this (with v22.x) all I get is an error as too many points are outside the valid projection error. This is because you are taking a map of the world and reprojecting it to a CRS designed for Wyoming.
It seems that ESRI are being "helpful" and clipping your output to the area of validity (assuming you meant something other than EPSG:32056). GeoTools assumes that you know what you are doing and doesn't do that, which is why you have all the countries of the world shown in that map.
Here is the output for just the USA, which suggests that the ESRI image you show is a different projection again (look at the 49th parallel).
I am trying to make a relatively simple game in Kotlin with force feedback. Every time an object hits a wall I want the phone to vibrate.
Here is my code, which generally works:
val vibT = 0L
val vibrator: Vibrator by lazy {
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
and then:
fun bump() {
var now = System.currentTimeMillis()
if ( vibrator.hasVibrator() && now>vibT+51) {
vibrator.cancel()
vibrator.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE))
vibT = now
}
}
It seems that when the code runs the bump() function in rapid succession (for example, when several objects hit the wall), it gets rather sluggish with the redraws. If I comment out the line vibrator.vibrate(...) then the game runs smoothly.
At first, I thought maybe I was just trying to call the function while it was still vibrating, hence causing a backlog of vibration requests, but even after adding in the timing check with now and vibT it's still a bit sluggish when a lot of calls are made to the function.
I'm assuming this is not a synchronous call, and that the code continues executing as soon as I've requested a vibration, but I can't find documentation to back that up.
Is there a better strategy? Maybe there are some gotcha's that I ought to be aware of when doing this?
After testing, I saw that calls to Vibrator::vibrate can take up to 5 milliseconds. If you're doing it in rapid succession, this can cause slowdown, as in your case.
I wrote a helper class that moves the calls to Vibrator::vibrate to a background thread to avoid blocking the UI thread:
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import java.util.concurrent.ArrayBlockingQueue
/**
* #author https://github.com/gottagofaster236
*/
class AsyncVibrator(context: Context) {
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
private val vibrationRequestsQueue = ArrayBlockingQueue<Long>(VIBRATOR_QUEUE_CAPACITY)
private var vibrationThread: Thread? = null
fun vibrate(lengthMs: Long) {
vibrationRequestsQueue.offer(lengthMs)
}
fun start() {
stop()
vibrationThread = Thread(::processVibrationRequests).apply(Thread::start)
}
fun stop() {
vibrationThread?.interrupt()
vibrationThread = null
}
private fun processVibrationRequests() {
while (!Thread.currentThread().isInterrupted) {
val lengthMs = try {
vibrationRequestsQueue.take()
} catch (e: InterruptedException) {
break
}
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(
VibrationEffect.createOneShot(
lengthMs,
VibrationEffect.DEFAULT_AMPLITUDE
)
)
} else {
#Suppress("DEPRECATION")
vibrator.vibrate(lengthMs)
}
}
}
private companion object {
const val VIBRATOR_QUEUE_CAPACITY = 10
}
}
Usage in an Activity:
// import ...
class MainActivity : Activity() {
private val asyncVibrator = AsyncVibrator(this)
// ...
override fun onResume() {
super.onResume()
asyncVibrator.start()
}
override fun onPause() {
asyncVibrator.stop()
super.onPause()
}
fun testVibration() {
// Vibrate for 100 milliseconds.
asyncVibrator.vibrate(100L)
}
}
Try this.
// this initiates the android vibrator [ put this on the onCreate ]
val vibe = this#MainActivity.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
then
// put this where you want the vibrator to activate (fun bump*())
val vibratorService = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibratorService.vibrate(40)
How to remove items from the storage (Storage) by clicking on the button. Elements are entered through the input and added to the page. New items are stored in the Storage. Now the situation is this - the elements are deleted on the page by clicking on the button, when I update the page, they still remain in place. They continue somewhere to be stored.
file home.html
<ion-list>
<ion-item *ngFor="let place of places ; let i = index"
(click)="onOpenPlace(place)">{{ place.title }}
</ion-item>
<button ion-button color="danger" (click)="deletePlace(i)">Delete</button>
</ion-list>
file home.ts
import { Component } from '#angular/core';
import { Storage } from '#ionic/storage'; /*** does not work ***/
import { ModalController, NavController } from 'ionic-angular';
import { NewPlacePage } from "../new-place/new-place";
import { PlacePage } from "../place/place";
import { PlacesService } from "../../services/places.service";
import { Place } from "../../model/place.model";
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
places: {title: string}[] = [];
constructor(
private storage: Storage,
public navCtrl: NavController,
private placesService: PlacesService,
private modalCtrl: ModalController) {
}
ionViewWillEnter() {
this.placesService.getPlaces()
.then(
(places) => this.places = places
);
}
onLoadNewPlace() {
this.navCtrl.push(NewPlacePage);
}
onOpenPlace(place: Place) {
this.modalCtrl.create(PlacePage, place).present();
}
deletePlace(i){ /*** does not work ***/
console.log('delete')
this.places.splice(i, 1);
}
}
file places.service.ts
import { Storage } from '#ionic/storage'; /*** does not work ***/
import { Injectable } from '#angular/core';
import { Place } from '../model/place.model';
#Injectable()
export class PlacesService {
private places: Place[] = [];
constructor ( private storage: Storage) {}
addPlace(place: Place) {
this.places.push(place);
this.storage.set('places', this.places);
}
deletePlace(place: Place){ /*** does not work ***/
this.storage.remove('places');
}
getPlaces() {
return this.storage.get('places')
.then(
(places) => {
this.places = places == null ? [] : places;
return this.places.slice();
}
);
}
}
The problem is that in your deletePlace(i) method, you're removing the item from your in memory array of places, but you're not updating the storage.
The deletePlace(...) method from your service won't work because you're saving the places in the storage as an array, so you can't remove a specific place.
deletePlace(place: Place) {
this.storage.remove('places');
}
I would fix it like this:
In your service, create a new method in order to update the storage with the changes you make to the in memory array:
saveChanges(places: Array<Place>) {
this.places = places;
this.storage.set('places', this.places);
}
And then in your component code, call that method after removing a place:
deletePlace(i) {
console.log('delete');
// Delete the place from the in memory array
this.places.splice(i, 1);
// Update the storage with the new list of places
this.placesService.saveChanges(this.places);
}
I've read up most solutions for this error and none seem to apply.
I'm running a basic AS3 app in FlashBuilder, on OS-X.
descriptor is set to extendedDesktop
have set the profile in FB to 'extendedDesktop'
am publishing as 'signed native installer'
I've tried launching the file from both:
app:/demo.sh
file:///Users/visualife/Desktop/AE/demo.sh
the target file is set to 777 (executable)
the target file runs fine when directly targetted
i'm running the exe on the same OS and machine it's created on
changing the 'demo.sh' file to a jpg etc doesn't change anything
No matter what I try I get told native process is support, everything runs fine until start is called then a Error: 3219 is thrown with no further information.
all help greatly appreciated!
I've included my code below:
package {
import flash.desktop.NativeProcess;
import flash.desktop.NativeProcessStartupInfo;
import flash.display.Sprite;
import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.NativeProcessExitEvent;
import flash.events.ProgressEvent;
import flash.filesystem.File;
import flash.text.TextField;
public class VauxhallController extends Sprite {
private var debug_txt:TextField;
public var process:NativeProcess;
private var sh:File;
public function VauxhallController() {
if (stage) {
init();
} else {
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init($e:Event=null):void {
this.removeEventListener(Event.ADDED_TO_STAGE, init);
build();
if (NativeProcess.isSupported) {
initListeners();
debugMe("Native process supported");
go();
} else {
debugMe("Native not supported");
}
}
private function build():void {
// debug
debug_txt = new TextField();
debug_txt.width = 300;
debug_txt.height= 600;
this.addChild(debug_txt);
}
private function initListeners():void { }
private function go():void {
runShellFile();
}
private function runShellFile():void {
debugMe("runShellFile");
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var essArgs:Vector.<String> = new Vector.<String>();
var file:File;
file = File.desktopDirectory.resolvePath("AE/demo.sh");
debugMe("path|"+ File.desktopDirectory.resolvePath("AE/demo.sh").url);
nativeProcessStartupInfo.executable = file;
nativeProcessStartupInfo.workingDirectory = File.desktopDirectory;
nativeProcessStartupInfo.executable = file;
process = new NativeProcess();
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(NativeProcessExitEvent.EXIT, onExit);
process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
try {
process.start(nativeProcessStartupInfo);
} catch (error:IllegalOperationError) {
debugMe(error.toString());
} catch (error:ArgumentError) {
debugMe(error.toString());
} catch (error:Error) {
debugMe(error.toString());
}
debugMe("# DONE");
}
public function onOutputData(event:ProgressEvent):void { debugMe("Got: "+ process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable)); }
public function onErrorData(event:ProgressEvent):void { debugMe("ERROR: "+ process.standardError.readUTFBytes(process.standardError.bytesAvailable)); }
public function onExit(event:NativeProcessExitEvent):void { debugMe("Process exited with: "+ event.exitCode); }
public function onIOError(event:IOErrorEvent):void { debugMe("IOError: "+ event.toString()); }
private function debugMe(_str:String):void { debug_txt.appendText(_str +"\n"); }
}
}
Have you read this article?
http://www.actionscripterrors.com/?p=2527
<supportedProfiles>extendedDesktop desktop</supportedProfiles>
I have the same error and in my case is because I'm trying to open .exe on MacOS. Verify if your demo.sh script interacts with .exe files.
i have a doubt..
i would like to create a function and it will look like this...
public class A //this is just a class file
{
function dowork()
{
//work 1
INPUT = here in this line it should call a delegate function or raise event etc...
//work 2 using INPUT
}
}
public class B
{
function myfn()
{
A objA = new A();
objA.dowork();
}
}
In the "Class A" we will raise event or so & it will display a windows form to user and then user will input some value & we need to return that value to Class A -> dowork method.... then only we should continue "work 2"
this should also support multi threading... anyone have idea how we can implement this??
thanks :)
You can use ManulResetEvent for this purpose: You run your input form and when it done that form set the event so you can catch it from A.dowork method. While the input in action you run the infinite loop, check event state and process application event to make you app responsible in this time:
public class A //this is just a class file
{
private ManualResetEvent _event;
public void dowork()
{
//work 1
_event = new ManualResetEvent(false);
//INPUT = here in this ...
Worker worker = new Worker();
worker.DoInput(_event);
while(true)
{
if(_event.WaitOne())
break;
Application.DoEvents();
}
//work 2 using INPUT
}
}
class Worker
{
private ManualResetEvent _event;
public void DoInput(ManualResetEvent #event)
{
_event = #event;
// Show input form here.
// When it done, you call: _event.Set();
}
}
Also, I suggest you (if you can) use Async library (it is available as a standalone setup). There you can implement it in much more straightforward way:
public class A //this is just a class file
{
public async void dowork()
{
//work 1
//INPUT = here in this ...
Worker worker = new Worker();
wait worker.DoInput();
//work 2 using INPUT
}
}
class Worker
{
public async void DoInput()
{
InputForm form = new InputForm();
wait form.ShowInput();
}
}
public class B
{
async void myfn()
{
A objA = new A();
wait objA.dowork();
}
}
As you see you just wait while other piece of code get executed without any UI locking and events.
I can provide deeper explanation of how async/wait works here if you need.