{ "id": "173996", "key": "TIMOB-27321", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [], "resolution": null, "resolutiondate": null, "created": "2019-08-08T14:57:22.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [ "engSchedule" ], "versions": [ { "id": "20827", "name": "Release 8.2.0", "archived": false, "released": true, "releaseDate": "2019-09-19" } ], "issuelinks": [ { "id": "57802", "type": { "id": "10001", "name": "Cloners", "inward": "is cloned into", "outward": "is cloned from" }, "outwardIssue": { "id": "173995", "key": "TIMOB-27320", "fields": { "summary": "iOS: Ti.Stream.writeStream fires callback for each loop of writes, no way to tell when done", "status": { "description": "The issue is open and ready for the assignee to start work on it.", "name": "Open", "id": "1", "statusCategory": { "id": 2, "key": "new", "colorName": "blue-gray", "name": "To Do" } }, "priority": { "name": "Medium", "id": "3" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } }, { "id": "57804", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "144873", "key": "TIMOB-18583", "fields": { "summary": "TiAPI: Create Node-compatible filesystem module API", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "High", "id": "2" }, "issuetype": { "id": "2", "description": "A new feature of the product, which has yet to be developed.", "name": "New Feature", "subtask": false } } } } ], "assignee": { "name": "amukherjee", "key": "amukherjee", "displayName": "Abir Mukherjee", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2020-11-23T18:05:01.000+0000", "status": { "description": "The issue is open and ready for the assignee to start work on it.", "name": "Open", "id": "1", "statusCategory": { "id": 2, "key": "new", "colorName": "blue-gray", "name": "To Do" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "Ti.Stream.pump behaves differently on iOS vs Android. If you register a pump callback handler that then writes the data out via Ti.Stream.write's async variant, the expected behavior is that the reads and writes get \"interleaved\" - which is what occurs on Android. On iOS, however, it will do all of the reads first until EOF, and then all of the writes. This can result in piling up Ti.Buffers queued for writes for the entire file contents.\r\n\r\nGiven code like this:\r\n\r\n{code:javascript}\r\n/**\r\n * This file is used to validate iOS test-cases. It is ran using the Xcode\r\n * project in titanium_mobile/iphone/iphone/Titanium.xcodeproj.\r\n *\r\n * Change the below code to fit your use-case. By default, it included a button\r\n * to trigger a log that is displayed in the Xcode console.\r\n */\r\n'use strict';\r\n\r\nvar win = Ti.UI.createWindow({\r\n\tbackgroundColor: '#fff'\r\n});\r\n\r\nvar btn = Ti.UI.createButton({\r\n\ttitle: 'Trigger'\r\n});\r\n\r\nbtn.addEventListener('click', function () {\r\n\tTi.API.info('Hello world!');\r\n\tpipe(Ti.Filesystem.resourcesDirectory + '/ti.main.js', Ti.Filesystem.tempDirectory + '/ti.main.js', err => {\r\n\t\tconsole.log(err);\r\n\t});\r\n});\r\n\r\nwin.add(btn);\r\nwin.open();\r\n\r\nfunction pipe(src, dest, callback) {\r\n\tconst srcFile = Ti.Filesystem.getFile(src);\r\n\tconst srcStream = srcFile.open(Ti.Filesystem.MODE_READ);\r\n\tconst destFile = Ti.Filesystem.getFile(dest);\r\n\tconst destStream = destFile.open(Ti.Filesystem.MODE_WRITE);\r\n\r\n\tlet bytesWritten = 0;\r\n\tlet totalBytes = -1;\r\n\tlet doneReading = false;\r\n\tTi.Stream.pump(srcStream, obj => {\r\n\t\tif (!obj.success) {\r\n\t\t\treturn callback(new Error(obj.error));\r\n\t\t}\r\n\r\n\t\tif (obj.bytesProcessed === -1) {\r\n\t\t\tconsole.log(`Finished reading. Total of ${obj.totalBytesProcessed} bytes`);\r\n\t\t\ttotalBytes = obj.totalBytesProcessed;\r\n\t\t\tdoneReading = true;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// schedule chunk to be written\r\n\t\tconsole.log(`Read ${obj.bytesProcessed} bytes`);\r\n\t\tTi.Stream.write(destStream, obj.buffer, 0, obj.buffer.length, writeObj => {\r\n\t\t\tif (!writeObj.success) {\r\n\t\t\t\treturn callback(new Error(writeObj.error));\r\n\t\t\t}\r\n\r\n\t\t\tconsole.log(`Read ${writeObj.bytesProcessed} bytes`);\r\n\t\t\tbytesWritten += writeObj.bytesProcessed;\r\n\r\n\t\t\t// if we're done reading and we've written as many bytes as we read, call the callback\r\n\t\t\tif (doneReading && bytesWritten === totalBytes) {\r\n\t\t\t\tconsole.log(`Finished writing. Total of ${bytesWritten} bytes`);\r\n\t\t\t\tcallback();\r\n\t\t\t}\r\n\t\t});\r\n\t}, 8092, true);\r\n}\r\n{code}\r\n\r\nOn iOS you'll see output like this:\r\n{code}\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 1970 bytes\r\n[INFO] Finished reading. Total of 228546 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Read 1970 bytes\r\n[INFO] Read 8092 bytes\r\n[INFO] Finished writing. Total of 228546 bytes\r\n[INFO] null\r\n{code}", "attachment": [], "flagged": false, "summary": "iOS: Ti.Stream.pump with Ti.Stream.write async operations don't actually run async/interleaved", "creator": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "subtasks": [], "reporter": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "environment": null, "comment": { "comments": [], "maxResults": 0, "total": 0, "startAt": 0 } } }