I'm trying to use an NSTextView to display high frequency updated logs but it's locking up the UI.
All logs are created in a different queue and only the minimal required updates occur on the main queue so I'm not sure how I can make this more efficient. I feel like I'm getting a leak because memory increases slowly but steadily over time.
If I turn off locking the app runs with minimal stable memory usage.
This is the function which updates the UI on the main queue. LogMessages are stored in an array which is kept at a fixed length on a 'background' queue and the most recent item is pushed to this function.
func logsChanged(message: LogMessage) {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
let dateString = formatter.stringFromDate(message.time)
let log = "[\(dateString)] " + message.text + "\n"
let attrs = [NSFontAttributeName : NSFont(name: "Courier New", size: 12.0)!]
let attributedLog = NSMutableAttributedString(string: log, attributes: attrs)
if let textStorage = self.logging.textStorage {
textStorage.beginEditing()
textStorage.appendAttributedString(attributedLog)
if textStorage.length > LogMaxSize {
textStorage.deleteCharactersInRange(NSMakeRange(0, attributedLog.length))
}
textStorage.endEditing()
self.logging.scrollToEndOfDocument(nil)
}
}
This is the code which calls UI update. I'm even slowing down updates by limiting to 20/sec
// do not let anything else write to the logs until this has completed.
dispatch_barrier_async(logAddQueue) {
let message = LogMessage(type: type, time: NSDate(), text: text)
if self.logMessages.count > self.maxMessages {
self.logMessages.removeLast()
}
self.logMessages.append(message)
if type == LogMessageType.Send || type == LogMessageType.Receive {
if NSDate().timeIntervalSinceDate(self.lastUpdatedUI) >= 0.05 {
// updates interface so run on the main queue
dispatch_async(GlobalMainQueue) {
self.delegate?.logsChanged(message)
}
self.lastUpdatedUI = NSDate()
}
} else {
// updates interface so run on the main queue
dispatch_async(GlobalMainQueue) {
self.delegate?.logsChanged(message)
}
self.lastUpdatedUI = NSDate()
}
}
Any suggestions?
Related
I use Java Selector for both server and client. For Server side it works perfect. It stops the thread when i call select() and wakes up when i change interest ops and it is ready for this operation..
But unfortunatelt it does not work for the same way for socket client. It stops the thread and does not wake up for reading or writing when i change interestedOps.
Creation of client connection:
selector = Selector.open()
SocketChannel.open().apply {
configureBlocking(false)
connect(address)
val key = socket.register(selector, SelectionKey.OP_READ or SelectionKey.OP_CONNECT)
val connection = ClientConnection(key) // Some stuff to hold the key for events
key.attach(connection)
}
Handle selection inside while loop:
val readyChannels = selector.select()
if (readyChannels == 0) continue
val keyIterator = selector.selectedKeys().iterator()
while (keyIterator.hasNext()) {
val key = keyIterator.next()
when (key.readyOps()) {
SelectionKey.OP_CONNECT -> {
val socket = (key.channel() as SocketChannel)
socket.finishConnect()
key.interestOps(key.interestOps() and SelectionKey.OP_CONNECT.inv())
// WORKS FINE!!!!!
key.interestOps(key.interestOps() and SelectionKey.OP_WRITE)
// Does not work at all. Selector will not wake up!
Thread(){
key.interestOps(key.interestOps() and SelectionKey.OP_WRITE)
}.start()
}
SelectionKey.OP_READ -> readPackets(key)
SelectionKey.OP_WRITE -> writePackets(key)
SelectionKey.OP_READ or SelectionKey.OP_WRITE -> {
writePackets(key)
readPackets(key)
}
}
keyIterator.remove()
}
So. The changing of interestOps from different thread does not work for socket clients. But it works fine for Server sockets..
Found solutions:
selector.select(300) -> use some timeout to wake up selector
selector.selectNow() -> use non blocking method and check the count of evetns
selector.wakeUp() -> save instance and wakeup it manually..
The question is Why it does not work ? Did I do some mistake? Something Missed?
UPD: Server side socket and selector
Creation of server socket:
selector = Selector.open()
serverSocket = ServerSocketChannel.open().apply {
socket().bind(address)
configureBlocking(false)
register(selector, SelectionKey.OP_ACCEPT)
}
Iteration of the selector inside Loop:
val readyChannels = selector.select()
if (readyChannels == 0) continue
val keyIterator = selector.selectedKeys().iterator()
while (keyIterator.hasNext()) {
val key = keyIterator.next()
when (key.readyOps()) {
SelectionKey.OP_ACCEPT -> {
val socket = serverSocket.accept().apply {
configureBlocking(false)
}
val client = clientFactory.createClient(selector,socket)
// Coroutines with Another thread context.
// There interestOps will be changed to send first data
_selectionAcceptFlow.tryEmit(client)
}
SelectionKey.OP_READ -> readPackets(key)
SelectionKey.OP_WRITE -> writePackets(key)
SelectionKey.OP_READ or SelectionKey.OP_WRITE -> {
writePackets(key)
readPackets(key)
}
}
keyIterator.remove()
}
If you call key.setInterestOps from a separate thread, you are creating a race condition between that call and the call to selector.select() in the client loop.
Your initial call to register does not contain SelectorKey.OP_WRITE. The first event triggered will be SelectorKey.OP_CONNECT. When handling that event, you indicate that in the future you are also interested in processing OP_WRITE.
If you do that in the same thread, then you are guaranteed that the interestOps are set the way you want them before the client loop reaches the call to selector.select(). If there is an OP_WRITE event available, you will process it immediatelly, otherwise the call blocks until it is available.
If you do that in a separate thread, then, depending on timing, you may run into a case where the client loop reaches the call to selector.select() and blocks even though there is an OP_WRITE event available. Since the separate thread did not yet change the interestOps, the OP_WRITE event is ignored.
I've included a self-contained example (client sending a message to server). To test different cases, you can comment/uncomment sections around line 90.
import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.SelectionKey
import java.nio.channels.Selector
import java.nio.channels.ServerSocketChannel
import java.nio.channels.SocketChannel
import java.util.concurrent.CountDownLatch
val address = InetSocketAddress("localhost", 5454)
fun main() {
val serverSocketSignal = CountDownLatch(1)
Thread {
startServer(serverSocketSignal)
}.start()
Thread {
startClient(serverSocketSignal)
}.start()
}
fun startServer(serverSocketSignal: CountDownLatch) {
//prepare server socket
val selector = Selector.open()
val serverSocket = ServerSocketChannel.open().apply {
socket().bind(address)
configureBlocking(false)
register(selector, SelectionKey.OP_ACCEPT)
}
serverSocketSignal.countDown();
//run server loop
while (true) {
println("Server loop")
val readyChannels = selector.select()
if (readyChannels == 0) continue
val keyIterator = selector.selectedKeys().iterator()
while (keyIterator.hasNext()) {
val key = keyIterator.next()
when (key.readyOps()) {
SelectionKey.OP_ACCEPT -> {
println("Server ACCEPT")
val socket = serverSocket.accept().apply {
configureBlocking(false)
}
socket.register(selector, SelectionKey.OP_READ)
}
SelectionKey.OP_READ -> {
val buffer = ByteBuffer.allocate(1024)
val count = (key.channel() as SocketChannel).read(buffer)
val message = String(buffer.array(), 0, count)
println("Server READ - " + message)
}
}
keyIterator.remove()
}
}
}
fun startClient(serverSocketSignal: CountDownLatch) {
serverSocketSignal.await();
//prepare client socket
val selector = Selector.open()
SocketChannel.open().apply {
configureBlocking(false)
connect(address)
register(selector, SelectionKey.OP_CONNECT or SelectionKey.OP_READ)
}
//run client loop
while (true) {
println("Client loop")
val readyChannels = selector.select()
if (readyChannels == 0) continue
val keyIterator = selector.selectedKeys().iterator()
while (keyIterator.hasNext()) {
val key = keyIterator.next()
when (key.readyOps()) {
SelectionKey.OP_CONNECT -> {
println("Client CONNECT")
val socket = (key.channel() as SocketChannel)
socket.finishConnect()
key.interestOpsAnd(SelectionKey.OP_CONNECT.inv())
/*
This works
*/
key.interestOps(SelectionKey.OP_WRITE)
/*
This doesn't work because we're And-ing the interestOps an the OP_WRITE op was not specified when calling register()
*/
// key.interestOpsAnd(SelectionKey.OP_WRITE)
/*
This may or may not work, depending on which thread gets executed first
- it will work if the setting interestOps=OP_WRITE in the new thread gets executed before the selector.select() in the client loop
- it will not work if selector.select() in the client loop gets executed before setting interestOps=OP_WRITE in the new thread,
since there won't be anything to process and the selector.select() gets blocked
On my machine, pausing the client loop even for a small duration was enough to change the result (e.g. the Thread.sleep(1) below).
* */
// Thread {
// println("Client setting interestedOps to OP_WRITE from new thread")
// key.interestOps(SelectionKey.OP_WRITE)
// }.start()
// //Thread.sleep(1)
}
SelectionKey.OP_WRITE -> {
println("Client WRITE")
val buffer = ByteBuffer.wrap("test message from client".toByteArray());
(key.channel() as SocketChannel).write(buffer)
key.interestOps(0)
}
}
keyIterator.remove()
}
}
}
As for why it works for you on the server side - you would have to share the full code for the server and client (might be a timing issue or your selector might be woken up by some events you did not intend to listen for). The snippets provided in the question do not contain enough infomation.
I was trying to implement live typeface change's in the edittextview
based on markdown syntax
and the first code I do was
my_edit_text_view.text = makeMDStyleSpannable(my_edit_text_view.text)//returns spannableString
but no luck since it is not invoking every time when text is changed
so I gave another try which is creating listener and testing things are working properly before I jump
my_edit_text_view.text = doOnTextChanged { it, start, count,after ->
if (it != null) {
if(it.isNotEmpty()){
Toast.makeText(this, (makeMDStyleSpannable(it)), Toast.LENGTH_SHORT).show()
}else if(it.isNullOrEmpty()){
}
}
}
as result it worked on another TOAST
However, here's the real recurring happened
my_edit_text_view.text = doOnTextChanged { it, start, count,after ->
if (it != null) {
if(it.isNotEmpty()){
my_edit_text_view.text = (makeMDStyleSpannable(it)
}else if(it.isNullOrEmpty()){
}
}
}
umm, what actually happening is while there is external text change(keyboard) on the edittext it calls makeMDStyleSpannable and this apply internal change then again and again it will call doOnTextChanged, Finally crashes.
How could I solve this problem?
keyboard(onText added[external]) => startThelistner => makeMDStyleSpannable[in]
/\ ||
||=====[infinite]=======
thanks
You can solve it by adding a boolean to check if text is changed by user or the code.
val shouldIgnoreChange = false
my_edit_text_view.text = doOnTextChanged { it, start, count,after ->
if (it != null) {
if(it.isNotEmpty()){
if(!shouldIgnoreChange){
shouldIgnoreChange = true
my_edit_text_view.text = (makeMDStyleSpannable(it)
shouldIgnoreChange = false
}
}else if(it.isNullOrEmpty()){
}
}
}
I would like to make a DM script acquiring camera images continuously, like VIEW mode. In my plan, the continious camera acquisition is start when START button located at an UIframe dialog is pressed; and a modeless dialog is also shown simultaneously. The continuous acuisition is stopped when the OK button located at the dialog is pressed. For this kind of scripts, I think a background thread is required. However, I don't have enough knowledge about such a background running.
It will be appreciated if you share some wisdom. Thank you very much in advance.
Continuous camera acquisition has actually nothing to do with background-threading in scripting, but rather requires setting the hardware into a continuous readout mode.
This functionality is no supported by the official scripting API up to now.
However, there exits an extended, object-oriented scripting API which gives a more in-depth control over cameras. You will need to be versatile in the object-oriented coding style of DM-scripting to use it, and you will need to get in contact with Gatan to possibly get access to this script API, as it is not officially supported.
You may want to use the support-request form on the Gatan homepage for this. (button on bottom of page)
Picking up on the kachigusa's comment to my other answer....
If you want to run a slow camera acquistion pseudo-continously, that is by performing repeated single readouts on a separate thread, then you may want to use a structure like the following:
class CMyBackgroundAction
{
number isRunning
CMyBackgroundAction(object self) { isRunning = 0; } // Initialisation in constructor
// methods to access flag from outside the object
void StopRunning(object self) { isRunning = 0; }
number GetIsRunning(object self) { return isRunning; }
// The stuff that should run in the background until the flag is set
void RunUntilBreak(object self)
{
Result("\n\n StartRunning")
isRunning = 1
while (isRunning)
{
Result( "\n New line..." )
sleep(0.5)
Result( "....done" )
}
Result("\n\n FINISHED")
}
}
class CmyDLG : UIframe
{
object backGroundRunObj
void OnStartStop( object self )
{
if ( !backGroundRunObj.GetIsRunning() )
{
// Nothing running in the background yet.
backGroundRunObj.StartThread( "RunUntilBreak" )
self.LookUpElement("StartStopButton").DLGTitle("Stop")
}
else
{
// It exists, so it is running. Just set the break-flag
backgroundRunObj.StopRunning();
self.LookUpElement("StartStopButton").DLGTitle("Start")
}
}
TagGroup BuildDLG( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog("StartStop",dlgItems)
dlgItems.DLGAddElement( DLGCreatePushButton( "Start", "OnStartStop" ).DLGIdentifier("StartStopButton" ) )
return dlg
}
Object Init(object self)
{
backGroundRunObj = Alloc(CMyBackgroundAction)
self.super.Init( self.BuildDLG() )
return self
}
}
Alloc(CmyDLG).Init().Display("Test")
There are of course several other constructs with a dialog governing a separate thread. This is just an example.
If you are going to start/stop something which performs in very regular intervals, but requires the main thread - another option would be to register/deregister periodic tasks from a dialog. Here is an example:
class CMyBackgroundAction
{
// The stuff that should run periodically
void RunUntilBreak(object self)
{
Result("\n Doing action #" + GetTime(1) )
}
}
class CmyDLG : UIframe
{
object backGroundRunObj
number taskID
void OnStartStop( object self )
{
if ( 0 == taskID )
{
// Start by registering the task ( every 0.5 sec)
taskID = AddMainThreadPeriodicTask(backGroundRunObj,"RunUntilBreak",0.5)
Result("\n STARTED ")
self.LookUpElement("StartStopButton").DLGTitle("Stop")
}
else
{
// Stop by removing the task
RemoveMainThreadTask( taskID )
Result("\n STOPPED ")
taskID = 0
self.LookUpElement("StartStopButton").DLGTitle("Start")
}
}
TagGroup BuildDLG( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog("StartStop",dlgItems)
dlgItems.DLGAddElement( DLGCreatePushButton( "Start", "OnStartStop" ).DLGIdentifier("StartStopButton" ) )
return dlg
}
Object Init(object self)
{
backGroundRunObj = Alloc(CMyBackgroundAction)
taskID = 0
return self.super.Init( self.BuildDLG() )
}
}
Alloc(CmyDLG).Init().Display("Test")
My app may create / delete thousands of managed objects while running. I have used secondary NSManagedObjectContexts(MOCs) with NSPrivateQueueConcurrencyType and NSOperations to make the app more responsive and most parts work well. But when I pressed ⌘Q and if the number of unsaved objects are large, the app hangs for quite a while before the window closes (the beach ball keeps on rotating...).
How to make the window disappear immediately, before the save of the MOC?
I tried to insert window.close() in applicationShouldTerminate in the AppDelegate, but it has no effect.
My code for deletion is nothing special, except the hierachy is really large. Something like
let items = self.items as! Set<Item>
Group.removeItems(items)
for i in items {
self.managedObjectContext?.deleteObject(i)
}
Item is a hierarchic entity. Group has a one-to-many relationship to items.
The removeItems is generated by CoreData with #NSManaged.
Many thanks.
Updates
I tried the following code, the save still blocks the UI.
#IBAction func quit(sender: AnyObject) {
NSRunningApplication.currentApplication().hide()
NSApp.terminate(sender)
}
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply
{
let op = NSBlockOperation { () -> Void in
do {
try self.managedObjectContext.save()
} catch {
print("error")
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
NSApp.replyToApplicationShouldTerminate(true)
})
}
op.start()
return .TerminateLater
}
This doesn't make the window close first, when the amount of created / deleted managed objects is large.
Then I changed to the following, as suggested by #bteapot. Still has no effect. The window still won't close immediately.
#IBAction func quit(sender: AnyObject) {
NSRunningApplication.currentApplication().hide()
NSApp.terminate(sender)
}
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply {
let op = NSBlockOperation { () -> Void in
self.managedObjectContext.performBlock({ () -> Void in
do {
try self.managedObjectContext.save()
} catch {
print("errr")
}
})
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
NSApp.replyToApplicationShouldTerminate(true)
})
}
dispatch_async ( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
{() -> Void in
op.start()
})
return .TerminateLater
}
Finally I sort of solved the problem, though the UI is still blocked sometimes, even with the same test data.
The approach used can be found here: https://blog.codecentric.de/en/2014/11/concurrency-coredata/ , Core Data background context best practice , https://www.cocoanetics.com/2012/07/multi-context-coredata/
First I made a backgroundMOC with .PrivateQueueConcurrencyType
lazy var backgroundMOC : NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
moc.undoManager = nil
return moc
}()
Then made it prent of the original moc.
lazy var managedObjectContext: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
// managedObjectContext.persistentStoreCoordinator = coordinator
managedObjectContext.parentContext = self.backgroundMOC
managedObjectContext.undoManager = nil
return managedObjectContext
}()
Two methods for the save.
func saveBackgroundMOC() {
self.backgroundMOC.performBlock { () -> Void in
do {
try self.backgroundMOC.save()
NSApp.replyToApplicationShouldTerminate(true)
} catch {
print("save error: bg")
}
}
}
func saveMainMOC() {
self.managedObjectContext.performBlock { () -> Void in
do {
try self.managedObjectContext.save()
self.saveBackgroundMOC()
} catch {
print("save error")
}
}
}
Change the applicationShouldTerminate() to
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply {
if !managedObjectContext.commitEditing() {
NSLog("\(NSStringFromClass(self.dynamicType)) unable to commit editing to terminate")
return .TerminateCancel
}
if !managedObjectContext.hasChanges {
return .TerminateNow
}
saveMainMOC()
return .TerminateLater
}
The reason it was so slow was I was using NSXMLStoreType instead of NSSQLiteStoreType.
Quitting an application might take a while since it will first empty the processes in queue.
Do you want immediate quit discarding everything in the Parent or children MOCs? But this will result in data loss.
If you have multi window application then, then close the window only but not quit the app.
Also thousands of entry should not take longer than 5 seconds to get processed and saved, if you have managed it properly. There could be some loopholes in your code, try to optimize using Instruments, CoreData profiler tool that would help you to understand the amount of time it is eating up.
To hide the window you can use the below, and in background all the coredata processing will happen, and once everything is done the app will terminate.
[self.window orderOut:nil];
The documentation for XCTest waitForExpectationsWithTimeout:handler:, states that
Only one -waitForExpectationsWithTimeout:handler: can be active at any given time, but multiple discrete sequences of { expectations -> wait } can be chained together.
However, I have no idea how to implement this, nor can I find any examples. I'm working on a class that first needs to find all available serial ports, pick the correct port and then connect to the device attached to that port. So, I'm working with at least two expectations, XCTestExpectation *expectationAllAvailablePorts and *expectationConnectedToDevice. How would I chain those two?
I do the following and it works.
expectation = [self expectationWithDescription:#"Testing Async Method Works!"];
[AsynClass method:parameter callbackFunction:^(BOOL callbackStatus, NSMutableArray* array) {
[expectation fulfil];
// whatever
}];
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
if (error) {
XCTFail(#"Expectation Failed with error: %#", error);
}
NSLog(#"expectation wait until handler finished ");
}];
// do it again
expectation = [self expectationWithDescription:#"Testing Async Method Works!"];
[CallBackClass method:parameter callbackFunction:^(BOOL callbackStatus, NSMutableArray* array) {
[expectation fulfil];
// whatever
}];
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
if (error) {
XCTFail(#"Expectation Failed with error: %#", error);
}
NSLog(#"expectation wait until handler finished ");
}];
swift
let expectation1 = //your definition
let expectation2 = //your definition
let result = XCTWaiter().wait(for: [expectation1, expectation2], timeout: 10, enforceOrder: true)
if result == .completed {
//all expectations completed in order
}
Assigning my expectation to a weak variable worked for me.
This seems to be working for me in Swift 3.0 as well.
let spyDelegate = SpyDelegate()
var asyncExpectation = expectation(description: "firstExpectation")
spyDelegate.asyncExpectation = asyncExpectation
let testee = MyClassToTest(delegate: spyDelegate)
testee.myFunction() //asyncExpectation.fulfill() happens here, implemented in SpyDelegate
waitForExpectations(timeout: 30.0) { (error: Error?) in
if let error = error {
XCTFail("error: \(error)")
}
}
asyncExpectation = expectation(description: "secoundExpectation")
spyDelegate.asyncExpectation = asyncExpectation
testee.delegate = spyDelegate
testee.myOtherFunction() //asyncExpectation.fulfill() happens here, implemented in SpyDelegate
waitForExpectations(timeout: 30.0) { (error: Error?) in
if let error = error {
XCTFail("error: \(error)")
}
}
Within a class that extends XCTestCase you can use wait(for:timeout:) like this:
let expectation1 = self.expectation(description: "expectation 1")
let expectation2 = self.expectation(description: "expectation 2")
let expectation3 = self.expectation(description: "expectation 3")
let expectation4 = self.expectation(description: "expectation 4")
// ...
// Do some asyc stuff, call expectation.fulfill() on each of the above expectations.
// ...
wait(for:[expectation1,expectation2,expectation3,expectation4], timeout: 8)