verdnatura-chat/ios/Pods/Flipper-Folly/folly/futures/Future-inl.h

2609 lines
80 KiB
C
Raw Normal View History

Merge beta into master (#2143) * [FIX] Messages being sent but showing as temp status (#1469) * [FIX] Missing messages after reconnect (#1470) * [FIX] Few fixes on themes (#1477) * [I18N] Missing German translations (#1465) * Missing German translation * adding a missing space behind colon * added a missing space after colon * and another attempt to finally fix this – got confused by all the branches * some smaller fixes for the translation * better wording * fixed another typo * [FIX] Crash while displaying the attached image with http on file name (#1401) * [IMPROVEMENT] Tap app and server version to copy to clipboard (#1425) * [NEW] Reply notification (#1448) * [FIX] Incorrect background color login on iPad (#1480) * [FIX] Prevent multiple tap on send (Share Extension) (#1481) * [NEW] Image Viewer (#1479) * [DOCS] Update Readme (#1485) * [FIX] Jitsi with Hermes Enabled (#1523) * [FIX] Draft messages not working with themed Messagebox (#1525) * [FIX] Go to direct message from members list (#1519) * [FIX] Make SAML wait for idp token instead of creating it on client (#1527) * [FIX] Server Test Push Notification (#1508) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Update to new server response (#1509) * [FIX] Insert messages with blank users (#1529) * Bump version to 4.2.1 (#1530) * [FIX] Error when normalizing empty messages (#1532) * [REGRESSION] CAS (#1570) * Bump version to 4.2.2 (#1571) * [FIX] Add username block condition to prevent error (#1585) * Bump version to 4.2.3 * Bump version to 4.2.4 * Bump version to 4.3.0 (#1630) * [FIX] Channels doesn't load (#1586) * [FIX] Channels doesn't load * [FIX] Update roomsUpdatedAt when subscriptions.length is 0 * [FIX] Remove unnecessary changes * [FIX] Improve the code Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Make SAML to work on Rocket.Chat < 2.3.0 (#1629) * [NEW] Invite links (#1534) * [FIX] Set the http-agent to the form that Rocket.Chat requires for logging (#1482) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] "Following thread" and "Unfollowed Thread" is hardcoded and not translated (#1625) * [FIX] Disable reset button if form didn't changed (#1569) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Header title of RoomInfoView (#1553) * [I18N] Gallery Permissions DE (#1542) * [FIX] Not allow to send messages to archived room (#1623) * [FIX] Profile fields automatically reset (#1502) * [FIX] Show attachment on ThreadMessagesView (#1493) * [NEW] Wordpress auth (#1633) * [CHORE] Add Start Packager script (#1639) * [CHORE] Update RN to 0.61.5 (#1638) * [CHORE] Update RN to 0.61.5 * [CHORE] Update react-native patch Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * Bump version to 4.3.1 (#1641) * [FIX] Change force logout rule (#1640) * Bump version to 4.4.0 (#1643) * [IMPROVEMENT] Use MessagingStyle on Android Notification (#1575) * [NEW] Request review (#1627) * [NEW] Pull to refresh RoomView (#1657) * [FIX] Unsubscribe from room (#1655) * [FIX] Server with subdirs (#1646) * [NEW] Clear cache (#1660) * [IMPROVEMENT] Memoize and batch subscriptions updates (#1642) * [FIX] Disallow empty sharing (#1664) * [REGRESSION] Use HTTPS links for sharing and markets protocol for review (#1663) * [FIX] In some cases, share extension doesn't load images (#1649) * [i18n] DE translations for new invite function and some minor fixes (#1631) * [FIX] Remove duplicate jetify step (#1628) minor: also remove 'cd' calls Co-authored-by: Diego Mello <diegolmello@gmail.com> * [REGRESSION] Read messages (#1666) * [i18n] German translations missing (#1670) * [FIX] Notifications crash on older Android Versions (#1672) * [i18n] Added Dutch translation (#1676) * [NEW] Omnichannel Beta (#1674) * [NEW] Confirm logout/clear cache (#1688) * [I18N] Add es-ES language (#1495) * [NEW] UiKit Beta (#1497) * [IMPROVEMENT] Use reselect (#1696) * [FIX] Notification in Android API level less than 24 (#1692) * [IMPROVEMENT] Send tmid on slash commands and media (#1698) * [FIX] Unhandled action on UIKit (#1703) * [NEW] Pull to refresh RoomsList (#1701) * [IMPROVEMENT] Reset app when language is changed (#1702) * [FIX] Small fixes on UIKit (#1709) * [FIX] Spotlight (#1719) * [CHORE] Update react-native-image-crop-picker (#1712) * [FIX] Messages Overlapping (Android) and MessageBox Scroll (iOS) (#1720) * [REGRESSION] Remove @ and # from mention (#1721) * [NEW] Direct message from user info (#1516) * [FIX] Delete slash commands (#1723) * [IMPROVEMENT] Hold URL to copy (#1684) * [FIX] Different sourcemaps generation for Hermes (#1724) * [FIX] Different sourcemaps generation for Hermes * Upload sourcemaps after build * [REVERT] Show emoji keyboard on Android (#1738) * [FIX] Stop logging react-native-image-crop-picker (#1745) * [FIX] Prevent toast ref error (#1744) * [FIX] Prevent reaction map error (#1743) * [FIX] Add missing calls to user info (#1741) * [FIX] Catch room unsubscribe error (#1739) * [i18n] Missing German keys (#1735) * [FIX] Missing i18n on MessagesView title (#1733) * [FIX] UIKit Modal: Weird behavior on Android Tablet (#1742) * [i18n] Missing key on German (#1747) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [i18n] Add Italian (#1736) * [CHORE] Memory leaks investigation (#1675) * [IMPROVEMENT] Alert verify email when enabled (#1725) * [NEW] Jitsi JWT added to URL (#1746) * [FIX] UIKit submit when connection lost (#1748) * Bump version to 4.5.0 (#1761) * [NEW] Default browser (#1752) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] HTTP Basic Auth (#1753) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Honor profile fields edit settings (#1687) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Room announcements (#1726) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Honor Register/Login settings (#1727) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Make links clickable on Room Info (#1730) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Hide system messages (#1755) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Honor "Message_AudioRecorderEnabled" (#1764) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [i18n] Missing de keys (#1765) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Redirect user to SetUsernameView (#1728) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Join Room (#1769) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Accept all media types using * (#1770) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Use RealName when necessary (#1758) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Markdown Line Break (#1783) * [IMPROVEMENT] Remove useMarkdown (#1774) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Open browser rather than webview on Create Workspace (#1788) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Markdown perf (#1796) * [FIX] Stop video when modal is closed (#1787) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Hide reply notification action when there are missing data (#1771) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [i18n] Added Japanese translation (#1781) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Reset password error message (#1772) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Close tablet modal (#1773) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Setting not present (#1775) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Thread header (#1776) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Keyboard tracking loses input ref (#1784) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Mark message as unread (#1785) Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [IMPROVEMENT] Log server version (#1786) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Add loading message on long running tasks (#1798) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Switch Apple account on Fastlane (#1810) * [FIX] Watermelon throwing "Cannot update a record with pending updates" (#1754) * [FIX] Detox tests (#1790) * [CHORE] Use markdown preview on RoomView Header (#1807) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] LoginSignup blink services (#1809) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Request user presence on demand (#1813) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Remove all invited users when create a channel (#1814) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Pop from room which you have been removed (#1819) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Room Info styles (#1820) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [i18n] Add missing German keys (#1800) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Empty mentions for @all and @here when real name is enabled (#1822) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [TESTS] Markdown added to Storybook (#1812) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [REGRESSION] Room View header title (#1827) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Storybook snapshots (#1831) Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [FIX] Mentions (#1829) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Thread message not found (#1830) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Separate delete and remove channel (#1832) * Rename to delete room * Separate delete and remove channel * handleRemoved -> handleRoomRemoved * [FIX] Navigate to RoomsList & Handle tablet case Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [NEW] Filter system messages per room (#1815) Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] e2e tests (#1838) * [FIX] Consecutive clear cache calls freezing app (#1851) * Bump version to 4.5.1 (#1853) * [FIX][iOS] Ignore silent mode on audio player (#1862) * [IMPROVEMENT] Create App Group property on Info.plist (#1858) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Make username clickable on message (#1618) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Show proper error message on profile (#1768) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Show toast when a message is starred/unstarred (#1616) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Incorrect size params to avatar endpoint (#1875) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Remove unrecognized emoji flags on android (#1887) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Remove react-native global installs (#1886) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Emojis transparent on android (#1881) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump acorn from 5.7.3 to 5.7.4 (#1876) Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4. - [Release notes](https://github.com/acornjs/acorn/releases) - [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump version to 4.6.0 (#1911) * [FIX] Encode Image URI (#1909) * [FIX] Encode Image URI * [FIX] Check if Image is Valid Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Adaptive Icons (#1904) * Remove unnecessary stuff from debug build * Adaptive icon for experimental app * [FIX] Stop showing message on leave channel (#1896) * [FIX] Leave room don't show 'was removed' message * [FIX] Remove duplicated code Co-authored-by: Diego Mello <diegolmello@gmail.com> * [i18n] Added missing German translations(#1900) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Linkedin OAuth login (#1913) * [CHORE] Fix typo in CreateChannel View (#1930) * [FIX] Respect protocol in HTTP Auth IPs (#1933) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Use new LinkedIn OAuth url (#1935) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Use storyboard on splash screen (#1939) * Update react-native-bootsplash * iOS * Fix android * [FIX] Check if avatar exists before create Icon (#1927) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Ignore self typing event (#1950) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Change default directory listing to Users (#1948) * fix: change default directory listing to Users * follow server settings * Fix state to props Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Onboarding layout (#1954) * Onboarding texts * OnboardingView * FormContainer * Minor fixes * NewServerView * Remove code * Refactor * WorkspaceView * Stash * Login with email working * Login with * Join open * Revert "Login with" This reverts commit d05dc507d2e9a2db76d433b9b1f62192eba35dbd. * Fix create account styles * Register * Refactor * LoginServices component * Refactor * Multiple servers * Remove native images * Refactor styles * Fix testid * Fix add server on tablet * i18n * Fix close modal * Fix TOTP * [FIX] Registration disabled * [FIX] Login Services separator * Fix logos * Fix AppVersion name * I18n * Minor fixes * [FIX] Custom Fields Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [NEW] Create discussions (#1942) * [WIP][NEW] Create Discussion * [FIX] Clear multiselect & Translations * [NEW] Create Discussion at MessageActions * [NEW] Disabled Multiselect * [FIX] Initial channel * [NEW] Create discussion on MessageBox Actions * [FIX] Crashing on edit name * [IMPROVEMENT] New message layout * [CHORE] Update README * [NEW] Avatars on MultiSelect * [FIX] Select Users * [FIX] Add redirect and Handle tablet * [IMPROVEMENT] Split CreateDiscussionView * [FIX] Create a discussion inner discussion * [FIX] Create a discussion * [I18N] Add pt-br * Change icons * [FIX] Nav to discussion & header title * Fix header Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Load messages (#1910) * Create updateLastOpen param on readMessages * Remove InteractionManager from load messages * [NEW] Custom Status (#1811) * [NEW] Custom Status * [FIX] Subscribe to changes * [FIX] Improve code using Banner component * [IMPROVEMENT] Toggle modal * [NEW] Edit custom status from Sidebar * [FIX] Modal when tablet * [FIX] Styles * [FIX] Switch to react-native-promp-android * [FIX] Custom Status UI * [TESTS] E2E Custom Status * Fix banner * Fix banner * Fix subtitle * status text * Fix topic header * Fix RoomActionsView topic * Fix header alignment on Android * [FIX] RoomInfo crashes when without statusText * [FIX] Use users.setStatus * [FIX] Remove customStatus of ProfileView * [FIX] Room View Thread Header Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] UI issues of Create Discussion View (#1965) * [NEW] Direct Message between multiple users (#1958) * [WIP] DM between multiple users * [WIP][NEW] Create new DM between multiple users * [IMPROVEMENT] Improve createChannel Sagas * [IMPROVEMENT] Selected Users view * [IMPROVEMENT] Room Actions of Group DM * [NEW] Create new DM between multiple users * [NEW] Group DM avatar * [FIX] Directory border * [IMPROVEMENT] Use isGroupChat * [CHORE] Remove legacy getRoomMemberId * [NEW] RoomTypeIcon * [FIX] No use legacy method on RoomInfoView * [FIX] Blink header when create new DM * [FIX] Only show create direct message option when allowed * [FIX] RoomInfoView * pt-BR * Few fixes * Create button name * Show create button only after a user is selected * Fix max users issues Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Add server and hide login (#1968) * Navigate to new server workspace from ServerDropdown if there's no token * Hide login button based on login services and Accounts_ShowFormLogin setting * [FIX] Lint Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [FIX] MultiSelect Keyboard behavior (Android) (#1969) * fixed-modal-position * made-changes Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [FIX] Bottom border style on DirectoryView (#1963) * [FIX] Border style * [FIX] Refactoring * [FIX] fix color of border * Undo Co-authored-by: Aroo <azhaubassar@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Clear settings on server change (#1967) * [FIX] Deeplinking without RoomId (#1925) * [FIX] Deeplinking without rid * [FIX] Join channel * [FIX] Deep linking without rid * Update app/lib/methods/canOpenRoom.js Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Two Factor authentication via email (#1961) * First api call working * [NEW] REST API Post wrapper 2FA * [NEW] Send 2FA on Email * [I18n] Add translations * [NEW] Translations & Cancel totp * [CHORE] Totp -> TwoFactor * [NEW] Two Factor by email * [NEW] Tablet Support * [FIX] Text colors * [NEW] Password 2fa * [FIX] Encrypt password on 2FA * [NEW] MethodCall2FA * [FIX] Password fallback * [FIX] Wrap all post/methodCall with 2fa * [FIX] Wrap missed function * few fixes * [FIX] Use new TOTP on Login * [improvement] 2fa methodCall Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [FIX] Correct message for manual approval user Registration (#1906) * [FIX] Correct message for manual approval from admin shown on Registeration * lint fix - added semicolon * Updated the translations * [FIX] Translations * i18n to match server Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Direct Message between multiple users REST (#1974) * [FIX] Investigate app losing connection issues (#1890) * [WIP] Reopen without timeOut & ping with 5 sec & Fix Unsubscribe * [FIX] Remove duplicated close * [FIX] Use no-dist lib * [FIX] Try minor fix * [FIX] Try reopen connection when app was put on foreground * [FIX] Remove timeout * [FIX] Build * [FIX] Patch * [FIX] Snapshot * [IMPROVEMENT] Decrease time to reopen * [FIX] Some fixes * [FIX] Update sdk version * [FIX] Subscribe Room Once * [CHORE] Update sdk * [FIX] Subscribe Room * [FIX] Try to resend missed subs * [FIX] Users never show status when start app without network * [FIX] Subscribe to room * [FIX] Multiple servers * [CHORE] Update SDK * [FIX] Don't duplicate streams on subscribeAll * [FIX] Server version when start the app offline * [FIX] Server version cached * [CHORE] Remove unnecessary code * [FIX] Offline server version * [FIX] Subscribe before connect * [FIX] Remove unncessary props * [FIX] Update sdk * [FIX] User status & Unsubscribe Typing * [FIX] Typing at incorrect room * [FIX] Multiple Servers * [CHORE] Update SDK * [REVERT] Undo some changes on SDK * [CHORE] Update sdk to prevent incorrect subscribes * [FIX] Prevent no reconnect * [FIX] Remove close on open * [FIX] Clear typing when disconnect/connect to SDK * [CHORE] Update SDK * [CHORE] Update SDK * Update SDK * fix merge develop Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Single message thread inserting thread without rid (#1999) * [FIX] ThreadMessagesView crashing on load (#1997) * [FIX] Saml (#1996) * [FIX] SAML incorrect close * [FIX] Pathname Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Change user own status (#1995) * [FIX] Change user own status * [IMPROVEMENT] Set activeUsers Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Loading all updated rooms after app resume (#1998) * [FIX] Loading all updated rooms after app resume * Fix room date on RoomItem Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Change notifications preferences (#2000) * [FIX] Change notifications preferences * [IMPROVEMENT] Picker View * [I18N] Translations * [FIX] Picker Selection * [FIX] List border * [FIX] Prevent crash * [FIX] Not-Pref tablet * [FIX] Use same style of LanguageView * [IMPROVEMENT] Send listItem title Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump version to 4.6.1 (#2001) * [FIX] DM header blink (#2011) * [FIX] Split get settings into two requests (#2017) * [FIX] Split get settings into two requests * [FIX] Clear settings only when change server * [IMPROVEMENT] Move the way to clear settings * [REVERT] Revert some changes * [FIX] Server Icon Co-authored-by: Diego Mello <diegolmello@gmail.com> * [REGRESSION] Invite Links (#2007) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Read only channel/broadcast (#1951) * [FIX] Read only channel/broadcast * [FIX] Roles missing * [FIX] Check roles to readOnly * [FIX] Can post * [FIX] Respect post-readonly permission * [FIX] Search a room readOnly Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Cas auth (#2024) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Login TOTP Compatibility to older servers (#2018) * [FIX] Login TOTP Compatibility to older servers * [FIX] Android crashes if use double negation Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump version to 4.6.4 (#2029) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Lint (#2030) * [FIX] UIKit with only one block (#2022) * [FIX] Message with only one block * [FIX] Update headers Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump version to 4.7.0 (#2035) * [FIX] Action Tint Color on Black theme (#2081) * [FIX] Prevent crash when thread is not found (#2080) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Prevent double click (#2079) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Show slash commands when disconnected (#2078) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Backhandler onboarding (#2077) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Respect UI_Allow_room_names_with_special_chars setting (#2076) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] RoomsList update sometimes isn't fired (#2071) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Stop inserting last message as message object from rooms stream if room is focused (#2069) * [IMPROVEMENT] No insert last message if the room is focused * fix discussion/threads Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Hide system messages (#2067) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Pending update (#2066) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Prevent crash when room.uids was not inserted yet (#2055) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FEATURE] Save video (#2063) * added-feature-save-video * fix sha256 Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Send totp-code to meteor call (#2050) * fixed-issue * removed-variable-name-errors * reverted-last-commit Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] MessageBox mention shouldn't show group DMs (#2049) * fixed-issue * [FIX] Filter users only if it's not a group chat Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] AttachmentView (Android)(Tablet) (#2047) * [fix]Tablet attachment View and Room Navigation * fix weird navigation and margin bottom Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Allow special chars in Filename (#2020) * fixed-filename-issue * improve Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Recorded audio on Android doesn't play on iOS (#2073) * react-native-video -> expo-av * remove react-native-video * Add audio mode * update mocks * [FIX] Loading bigger than play/pause Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Message Touchable (#2082) * [FIX] Avatar touchable * [IMPROVEMENT] onLongPress on all Message Touchables * [IMPROVEMENT] User & baseUrl on MessageContext * [FIX] Context Access * [FIX] BaseURL * Fix User Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] ReactionsModal (#2085) * [NEW] Delete Server (#1975) * [NEW] Delete server Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> * [FIX] Revert removed function Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> * pods * i18n * Revert "pods" This reverts commit 2854a1650538159aeeafe90fdb2118d12b76a82f. Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Change server while connecting/updating (#1981) * [IMPROVEMENT] Change server while connecting * [FIX] Not login/reconnect to previous server * [FIX] Abort all fetch while connecting * [FIX] Abort sdk fetch * [FIX] Patch-package * Add comments Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Keep screen awake while recording/playing some audio (#2089) * [IMPROVEMENT] Keep screen awake while recording/playing some audio * [FIX] Add expo-keep-awake mock * [FIX] UIKit crashing when UIKitModal receive update event (#2088) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Close announcement banner (#2064) * [NEW] Created new field in subscription table Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * [NEW] New field added to obeserver in room view Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * [NEW] Added icon and new design to banner Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * [NEW] Close banner function works Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * [IMPROVEMENT] closed banner status now update correctly Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com> * improve banner style Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * Update all dependencies (#2008) * Android RN 62 * First steps iOS * Second step iOS * iOS compiling * "New" build system * Finish iOS * Flipper * Update to RN 0.62.1 * expo libs * Hermes working * Fix lint * Fix android build * Patches * Dev patches * Patch WatermelonDB: https://github.com/Nozbe/WatermelonDB/pull/660 * Fix jitsi * Update several minors * Update dev minors and lint * react-native-keyboard-input * Few updates * device info * react-native-fast-image * Navigation bar color * react-native-picker-select * webview * reactotron-react-native * Watermelondb * RN 0.62.2 * Few updates * Fix selection * update gems * remove lib * finishing * tests * Use node 10 * Re-enable app bundle * iOS build * Update jitsi ios * [NEW] Passcode and biometric unlock (#2059) * Update expo libs * Configure expo-local-authentication * ScreenLockedView * Authenticate server change * Auth on app resume * localAuthentication util * Add servers.lastLocalAuthenticatedSession column * Save last session date on background * Use our own version of app state redux * Fix libs * Remove inactive * ScreenLockConfigView * Apply on saved data * Auto lock option label * Starting passcode * Basic passcode flow working * Change passcode * Check if biometry is enrolled * Use fork * Migration * Patch expo-local-authentication * Use async storage * Styling * Timer * Refactor * Lock orientation portrait when not on tablet * share extension * Deep linking * Share extension * Refactoring passcode * use state * Stash * Refactor * Change passcode * Animate dots on error * Matching passcodes * Shake * Remove lib * Delete button * Fade animation on modal * Refactoring * ItemInfo * I18n * I18n * Remove unnecessary prop * Save biometry column * Raise time to lock to 30 seconds * Vibrate on wrong confirmation passcode * Reset attempts and save last authentication on local passcode confirmation * Remove inline style * Save last auth * Fix header blink * Change function name * Fix android modal * Fix vibration permission * PasscodeEnter calls biometry * Passcode on the state * Biometry button on PasscodeEnter * Show whole passcode * Secure passcode * Save passcode with promise to prevent empty passcodes and immediately lock * Patch expo-local-authentication * I18n * Fix biometry being called every time * Blur screen on app inactive * Revert "Blur screen on app inactive" This reverts commit a4ce812934adcf6cf87eb1a92aec9283e2f26753. * Remove immediately because of how Activities work on Android * Pods * New layout * stash * Layout refactored * Fix icons * Force set passcode from server * Lint * Improve permission message * Forced passcode subtitle * Disable based on admin's choice * Require local authentication on login success * Refactor * Update tests * Update react-native-device-info to fix notch * Lint * Fix modal * Fix icons * Fix min auto lock time * Review * keep enabled on mobile * fix forced by admin when enable unlock with passcode * use DEFAULT_AUTO_LOCK when manual enable screenLock * fix check has passcode * request biometry on first password * reset auto time lock when disabled on server Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> * [FIX] Messages View (#2090) * [FIX] Messages View * [FIX] Opening PDF from Files View * [FIX] Audio * [FIX] SearchMessagesView Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Big names overflow (#2072) * [FIX] Big names overflow * [FIX] Message time Co-authored-by: devyaniChoubey <devyanichoubey16@gmail.com> * [FIX] Some alignments * fix user item overflow * some adjustments Co-authored-by: devyaniChoubey <devyanichoubey16@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Avatar of message as an emoji (#2038) * fixed-issue * removed-hardcoded-emoji * Merge develop * replaced markdown with emoji componenent * made-changes * use avatar onPress Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Livechat (#2004) * [WIP][NEW] Livechat info/actions * [IMPROVEMENT] RoomActionsView * [NEW] Visitor Navigation * [NEW] Get Department REST * [FIX] Borders * [IMPROVEMENT] Refactor RoomInfo View * [FIX] Error while navigate from mention -> roomInfo * [NEW] Livechat Fields * [NEW] Close Livechat * [WIP] Forward livechat * [NEW] Return inquiry * [WIP] Comment when close livechat * [WIP] Improve roomInfo * [IMPROVEMENT] Forward room * [FIX] Department picker * [FIX] Picker without results * [FIX] Superfluous argument * [FIX] Check permissions on RoomActionsView * [FIX] Livechat permissions * [WIP] Show edit to livechat * [I18N] Add pt-br translations * [WIP] Livechat Info * [IMPROVEMENT] Livechat info * [WIP] Livechat Edit * [WIP] Livechat edit * [WIP] Livechat Edit * [WIP] Livechat edit scroll * [FIX] Edit customFields * [FIX] Clean livechat customField * [FIX] Visitor Navigation * [NEW] Next input logic LivechatEdit * [FIX] Add livechat data to subscription * [FIX] Revert change * [NEW] Livechat user Status * [WIP] Livechat tags * [NEW] Edit livechat tags * [FIX] Prevent some crashes * [FIX] Forward * [FIX] Return Livechat error * [FIX] Prevent livechat info crash * [IMPROVEMENT] Use input style on forward chat * OnboardingSeparator -> OrSeparator * [FIX] Go to next input * [NEW] Added some icons * [NEW] Livechat close * [NEW] Forward Room Action * [FIX] Livechat edit style * [FIX] Change status logic * [CHORE] Remove unnecessary logic * [CHORE] Remove unnecessary code * [CHORE] Remove unecessary case * [FIX] Superfluous argument * [IMPROVEMENT] Submit livechat edit * [CHORE] Remove textInput type * [FIX] Livechat edit * [FIX] Livechat Edit * [FIX] Use same effect * [IMPROVEMENT] Tags input * [FIX] Add empty tag * Fix minor issues * Fix typo * insert livechat room data to our room object * review * add method calls server version Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Delete Subs (#2091) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Android build (#2094) * [FIX] Blink header DM (#2093) * [FIX] Blink header DM * Remove query * [FIX] Push RoomInfoView * remove unnecessary try/catch * [FIX] RoomInfo > Message (Tablet) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Default biometry enabled (#2095) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Enable navigating to a room from auth deep linking (#2115) * Wait for login success to navigate * Enable auth and room deep linking at the same time * [FIX] NewMessageView Press Item should open DM (#2116) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Roles throwing error (#2110) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Wait attach activity before changeNavigationBarColor (#2111) * [FIX] Wait attach activity before changeNavigationBarColor * Remove timeout and add try/catch Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] UIKit crash when some app send a list (#2117) * [FIX] StoryBook * [FIX] UIKit crash when some app send a list * [CHORE] Update snapshot * [CHORE] Remove token & id * [FIX] Change bar color while no activity attached (#2130) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Screen Lock options i18n (#2120) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [i18n] Added missing German translation strings (#2105) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Sometimes SDK is null when try to connect (#2131) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Autocomplete position on Android (#2106) * [FIX] Autocomplete position on Android * [FIX] Set selection to 0 when needed Co-authored-by: Diego Mello <diegolmello@gmail.com> * Revert "[FIX] Autocomplete position on Android (#2106)" (#2136) This reverts commit e8c38d6f6f69ae396a4aae6e37336617da739a6d. * [FIX] Here and all mentions shouldn't refer to users (#2137) * [FIX] No send data to bugsnag if it's an aborted request (#2133) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [TESTS] Update and separate E2E tests (#2126) * Tests passing until roomslist * create room * roominfo * change server * broadcast * profile * custom status * forgot password * working * room and onboarding * Tests separated * config.yml refactor * Revert "config.yml refactor" This reverts commit 0e984d3029e47612726bf199553f7abdf24843e5. * CI * lint * CI refactor * Onboarding tests * npx detox * Add all tests * Save brew cache * mac-env executor * detox-test command * Update readme * Remove folder * [FIX] Screen Lock Time respect local value (#2141) * [FIX] Screen Lock Time respect local value * [FIX] Enable biometry at the first passcode change Co-authored-by: phriedrich <info@phriedrich.de> Co-authored-by: Guilherme Siqueira <guilhersiqueira@gmail.com> Co-authored-by: Prateek Jain <44807945+Prateek93a@users.noreply.github.com> Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: Prateek Jain <prateek93a@gmail.com> Co-authored-by: devyaniChoubey <52153085+devyaniChoubey@users.noreply.github.com> Co-authored-by: Bernard Seow <ssbing99@gmail.com> Co-authored-by: Hiroki Ishiura <ishiura@ja2.so-net.ne.jp> Co-authored-by: Exordian <jakob.englisch@gmail.com> Co-authored-by: Daanchaam <daanhendriks97@gmail.com> Co-authored-by: Youssef Muhamad <emaildeyoussefmuhamad@gmail.com> Co-authored-by: Iván Álvarez <ialvarezpereira@gmail.com> Co-authored-by: Sarthak Pranesh <41206172+sarthakpranesh@users.noreply.github.com> Co-authored-by: Michele Pellegrini <pellettiero@users.noreply.github.com> Co-authored-by: Tanmoy Bhowmik <tanmoy.openroot@gmail.com> Co-authored-by: Hibikine Kage <14365761+hibikine@users.noreply.github.com> Co-authored-by: Ezequiel de Oliveira <ezequiel1de1oliveira@gmail.com> Co-authored-by: Neil Agarwal <neil@neilagarwal.me> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Govind Dixit <GOVINDDIXIT93@GMAIL.COM> Co-authored-by: Zhaubassarova Aruzhan <49000079+azhaubassar@users.noreply.github.com> Co-authored-by: Aroo <azhaubassar@gmail.com> Co-authored-by: Sarthak Pranesh <sarthak.pranesh2018@vitstudent.ac.in> Co-authored-by: Siddharth Padhi <padhisiddharth31@gmail.com> Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> Co-authored-by: devyaniChoubey <devyanichoubey16@gmail.com>
2020-05-25 20:54:27 +00:00
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <thread>
#include <utility>
#include <folly/Optional.h>
#include <folly/Traits.h>
#include <folly/detail/AsyncTrace.h>
#include <folly/executors/ExecutorWithPriority.h>
#include <folly/executors/GlobalExecutor.h>
#include <folly/executors/InlineExecutor.h>
#include <folly/executors/QueuedImmediateExecutor.h>
#include <folly/futures/detail/Core.h>
#include <folly/synchronization/Baton.h>
#if FOLLY_FUTURE_USING_FIBER
#include <folly/fibers/Baton.h>
#endif
namespace folly {
class Timekeeper;
namespace futures {
namespace detail {
#if FOLLY_FUTURE_USING_FIBER
typedef folly::fibers::Baton FutureBatonType;
#else
typedef folly::Baton<> FutureBatonType;
#endif
} // namespace detail
} // namespace futures
namespace detail {
std::shared_ptr<Timekeeper> getTimekeeperSingleton();
} // namespace detail
namespace futures {
namespace detail {
// InvokeResultWrapper and wrapInvoke enable wrapping a result value in its
// nearest Future-type counterpart capable of also carrying an exception.
// e.g.
// (semi)Future<T> -> (semi)Future<T> (no change)
// Try<T> -> Try<T> (no change)
// void -> Try<folly::Unit>
// T -> Try<T>
template <typename T>
struct InvokeResultWrapperBase {
template <typename F>
static T wrapResult(F fn) {
return T(fn());
}
static T wrapException(exception_wrapper&& e) {
return T(std::move(e));
}
};
template <typename T>
struct InvokeResultWrapper : InvokeResultWrapperBase<Try<T>> {};
template <typename T>
struct InvokeResultWrapper<Try<T>> : InvokeResultWrapperBase<Try<T>> {};
template <typename T>
struct InvokeResultWrapper<SemiFuture<T>>
: InvokeResultWrapperBase<SemiFuture<T>> {};
template <typename T>
struct InvokeResultWrapper<Future<T>> : InvokeResultWrapperBase<Future<T>> {};
template <>
struct InvokeResultWrapper<void> : InvokeResultWrapperBase<Try<Unit>> {
template <typename F>
static Try<Unit> wrapResult(F fn) {
fn();
return Try<Unit>(unit);
}
};
template <typename T, typename F>
auto wrapInvoke(folly::Try<T>&& t, F&& f) {
auto fn = [&]() {
return std::forward<F>(f)(
t.template get<
false,
typename futures::detail::valueCallableResult<T, F>::FirstArg>());
};
using FnResult = decltype(fn());
using Wrapper = InvokeResultWrapper<FnResult>;
if (t.hasException()) {
return Wrapper::wrapException(std::move(t).exception());
}
return Wrapper::wrapResult(fn);
}
// Guarantees that the stored functor is destructed before the stored promise
// may be fulfilled. Assumes the stored functor to be noexcept-destructible.
template <typename T, typename F>
class CoreCallbackState {
using DF = std::decay_t<F>;
public:
CoreCallbackState(Promise<T>&& promise, F&& func) noexcept(
noexcept(DF(std::declval<F&&>())))
: func_(std::forward<F>(func)), promise_(std::move(promise)) {
assert(before_barrier());
}
CoreCallbackState(CoreCallbackState&& that) noexcept(
noexcept(DF(std::declval<F&&>()))) {
if (that.before_barrier()) {
new (&func_) DF(std::forward<F>(that.func_));
promise_ = that.stealPromise();
}
}
CoreCallbackState& operator=(CoreCallbackState&&) = delete;
~CoreCallbackState() {
if (before_barrier()) {
stealPromise();
}
}
template <typename... Args>
auto invoke(Args&&... args) noexcept(
noexcept(std::declval<F&&>()(std::declval<Args&&>()...))) {
assert(before_barrier());
return std::forward<F>(func_)(std::forward<Args>(args)...);
}
template <typename... Args>
auto tryInvoke(Args&&... args) noexcept {
return makeTryWith([&] { return invoke(std::forward<Args>(args)...); });
}
void setTry(Executor::KeepAlive<>&& keepAlive, Try<T>&& t) {
stealPromise().setTry(std::move(keepAlive), std::move(t));
}
void setException(Executor::KeepAlive<>&& keepAlive, exception_wrapper&& ew) {
setTry(std::move(keepAlive), Try<T>(std::move(ew)));
}
Promise<T> stealPromise() noexcept {
assert(before_barrier());
func_.~DF();
return std::move(promise_);
}
private:
bool before_barrier() const noexcept {
return !promise_.isFulfilled();
}
union {
DF func_;
};
Promise<T> promise_{Promise<T>::makeEmpty()};
};
template <typename T, typename F>
auto makeCoreCallbackState(Promise<T>&& p, F&& f) noexcept(
noexcept(CoreCallbackState<T, F>(
std::declval<Promise<T>&&>(),
std::declval<F&&>()))) {
return CoreCallbackState<T, F>(std::move(p), std::forward<F>(f));
}
template <typename T, typename R, typename... Args>
auto makeCoreCallbackState(Promise<T>&& p, R (&f)(Args...)) noexcept {
return CoreCallbackState<T, R (*)(Args...)>(std::move(p), &f);
}
template <class T>
FutureBase<T>::FutureBase(SemiFuture<T>&& other) noexcept : core_(other.core_) {
other.core_ = nullptr;
}
template <class T>
FutureBase<T>::FutureBase(Future<T>&& other) noexcept : core_(other.core_) {
other.core_ = nullptr;
}
template <class T>
template <class T2, typename>
FutureBase<T>::FutureBase(T2&& val)
: core_(Core::make(Try<T>(std::forward<T2>(val)))) {}
template <class T>
template <typename T2>
FutureBase<T>::FutureBase(
typename std::enable_if<std::is_same<Unit, T2>::value>::type*)
: core_(Core::make(Try<T>(T()))) {}
template <class T>
void FutureBase<T>::assign(FutureBase<T>&& other) noexcept {
detach();
core_ = std::exchange(other.core_, nullptr);
}
template <class T>
FutureBase<T>::~FutureBase() {
detach();
}
template <class T>
T& FutureBase<T>::value() & {
return result().value();
}
template <class T>
T const& FutureBase<T>::value() const& {
return result().value();
}
template <class T>
T&& FutureBase<T>::value() && {
return std::move(result().value());
}
template <class T>
T const&& FutureBase<T>::value() const&& {
return std::move(result().value());
}
template <class T>
Try<T>& FutureBase<T>::result() & {
return getCoreTryChecked();
}
template <class T>
Try<T> const& FutureBase<T>::result() const& {
return getCoreTryChecked();
}
template <class T>
Try<T>&& FutureBase<T>::result() && {
return std::move(getCoreTryChecked());
}
template <class T>
Try<T> const&& FutureBase<T>::result() const&& {
return std::move(getCoreTryChecked());
}
template <class T>
bool FutureBase<T>::isReady() const {
return getCore().hasResult();
}
template <class T>
bool FutureBase<T>::hasValue() const {
return result().hasValue();
}
template <class T>
bool FutureBase<T>::hasException() const {
return result().hasException();
}
template <class T>
void FutureBase<T>::detach() {
if (core_) {
core_->detachFuture();
core_ = nullptr;
}
}
template <class T>
void FutureBase<T>::throwIfInvalid() const {
if (!core_) {
throw_exception<FutureInvalid>();
}
}
template <class T>
void FutureBase<T>::throwIfContinued() const {
if (!core_ || core_->hasCallback()) {
throw_exception<FutureAlreadyContinued>();
}
}
template <class T>
Optional<Try<T>> FutureBase<T>::poll() {
auto& core = getCore();
return core.hasResult() ? Optional<Try<T>>(std::move(core.getTry()))
: Optional<Try<T>>();
}
template <class T>
void FutureBase<T>::raise(exception_wrapper exception) {
getCore().raise(std::move(exception));
}
template <class T>
void FutureBase<T>::setCallback_(
CoreCallback&& func,
futures::detail::InlineContinuation allowInline) {
throwIfContinued();
getCore().setCallback(
std::move(func), RequestContext::saveContext(), allowInline);
}
template <class T>
FutureBase<T>::FutureBase(futures::detail::EmptyConstruct) noexcept
: core_(nullptr) {}
// MSVC 2017 Update 7 released with a bug that causes issues expanding to an
// empty parameter pack when invoking a templated member function. It should
// be fixed for MSVC 2017 Update 8.
// TODO: Remove.
namespace detail_msvc_15_7_workaround {
template <typename R, std::size_t S>
using IfArgsSizeIs = std::enable_if_t<R::Arg::ArgsSize::value == S, int>;
template <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0>
decltype(auto)
invoke(R, State& state, Executor::KeepAlive<>&&, Try<T>&& /* t */) {
return state.invoke();
}
template <typename R, typename State, typename T, IfArgsSizeIs<R, 2> = 0>
decltype(auto) invoke(R, State& state, Executor::KeepAlive<>&& ka, Try<T>&& t) {
using Arg1 = typename R::Arg::ArgList::Tail::FirstArg;
return state.invoke(
std::move(ka), std::move(t).template get<R::Arg::isTry(), Arg1>());
}
template <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0>
decltype(auto)
tryInvoke(R, State& state, Executor::KeepAlive<>&&, Try<T>&& /* t */) {
return state.tryInvoke();
}
template <typename R, typename State, typename T, IfArgsSizeIs<R, 2> = 0>
decltype(auto)
tryInvoke(R, State& state, Executor::KeepAlive<>&& ka, Try<T>&& t) {
using Arg1 = typename R::Arg::ArgList::Tail::FirstArg;
return state.tryInvoke(
std::move(ka), std::move(t).template get<R::Arg::isTry(), Arg1>());
}
} // namespace detail_msvc_15_7_workaround
// then
// Variant: returns a value
// e.g. f.then([](Try<T>&& t){ return t.value(); });
template <class T>
template <typename F, typename R>
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
FutureBase<T>::thenImplementation(
F&& func,
R,
futures::detail::InlineContinuation allowInline) {
static_assert(R::Arg::ArgsSize::value == 2, "Then must take two arguments");
typedef typename R::ReturnsFuture::Inner B;
Promise<B> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
// grab the Future now before we lose our handle on the Promise
auto sf = p.getSemiFuture();
sf.setExecutor(folly::Executor::KeepAlive<>{this->getExecutor()});
auto f = Future<B>(sf.core_);
sf.core_ = nullptr;
/* This is a bit tricky.
We can't just close over *this in case this Future gets moved. So we
make a new dummy Future. We could figure out something more
sophisticated that avoids making a new Future object when it can, as an
optimization. But this is correct.
core_ can't be moved, it is explicitly disallowed (as is copying). But
if there's ever a reason to allow it, this is one place that makes that
assumption and would need to be fixed. We use a standard shared pointer
for core_ (by copying it in), which means in essence obj holds a shared
pointer to itself. But this shouldn't leak because Promise will not
outlive the continuation, because Promise will setException() with a
broken Promise if it is destructed before completed. We could use a
weak pointer but it would have to be converted to a shared pointer when
func is executed (because the Future returned by func may possibly
persist beyond the callback, if it gets moved), and so it is an
optimization to just make it shared from the get-go.
Two subtle but important points about this design. futures::detail::Core
has no back pointers to Future or Promise, so if Future or Promise get
moved (and they will be moved in performant code) we don't have to do
anything fancy. And because we store the continuation in the
futures::detail::Core, not in the Future, we can execute the continuation
even after the Future has gone out of scope. This is an intentional design
decision. It is likely we will want to be able to cancel a continuation
in some circumstances, but I think it should be explicit not implicit
in the destruction of the Future used to create it.
*/
this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
if (!R::Arg::isTry() && t.hasException()) {
state.setException(std::move(ka), std::move(t.exception()));
} else {
auto propagateKA = ka.copy();
state.setTry(std::move(propagateKA), makeTryWith([&] {
return detail_msvc_15_7_workaround::invoke(
R{}, state, std::move(ka), std::move(t));
}));
}
},
allowInline);
return f;
}
// Pass through a simple future as it needs no deferral adaptation
template <class T>
Future<T> chainExecutor(Executor::KeepAlive<>, Future<T>&& f) {
return std::move(f);
}
// Correctly chain a SemiFuture for deferral
template <class T>
Future<T> chainExecutor(Executor::KeepAlive<> e, SemiFuture<T>&& f) {
if (!e) {
e = folly::getKeepAliveToken(InlineExecutor::instance());
}
return std::move(f).via(e);
}
// Variant: returns a Future
// e.g. f.then([](T&& t){ return makeFuture<T>(t); });
template <class T>
template <typename F, typename R>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
FutureBase<T>::thenImplementation(
F&& func,
R,
futures::detail::InlineContinuation allowInline) {
static_assert(R::Arg::ArgsSize::value == 2, "Then must take two arguments");
typedef typename R::ReturnsFuture::Inner B;
Promise<B> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
// grab the Future now before we lose our handle on the Promise
auto sf = p.getSemiFuture();
auto e = getKeepAliveToken(this->getExecutor());
sf.setExecutor(std::move(e));
auto f = Future<B>(sf.core_);
sf.core_ = nullptr;
this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
if (!R::Arg::isTry() && t.hasException()) {
state.setException(std::move(ka), std::move(t.exception()));
} else {
// Ensure that if function returned a SemiFuture we correctly chain
// potential deferral.
auto tf2 = detail_msvc_15_7_workaround::tryInvoke(
R{}, state, ka.copy(), std::move(t));
if (tf2.hasException()) {
state.setException(std::move(ka), std::move(tf2.exception()));
} else {
auto statePromise = state.stealPromise();
auto tf3 = chainExecutor(std::move(ka), *std::move(tf2));
std::exchange(statePromise.core_, nullptr)
->setProxy(std::exchange(tf3.core_, nullptr));
}
}
},
allowInline);
return f;
}
class WaitExecutor final : public folly::Executor {
public:
void add(Func func) override {
auto wQueue = queue_.wlock();
if (wQueue->detached) {
return;
}
bool empty = wQueue->funcs.empty();
wQueue->funcs.push_back(std::move(func));
if (empty) {
baton_.post();
}
}
void drive() {
baton_.wait();
#if FOLLY_FUTURE_USING_FIBER
fibers::runInMainContext([&]() {
#endif
baton_.reset();
auto funcs = std::move(queue_.wlock()->funcs);
for (auto& func : funcs) {
std::exchange(func, nullptr)();
}
#if FOLLY_FUTURE_USING_FIBER
});
#endif
}
using Clock = std::chrono::steady_clock;
bool driveUntil(Clock::time_point deadline) {
if (!baton_.try_wait_until(deadline)) {
return false;
}
#if FOLLY_FUTURE_USING_FIBER
return fibers::runInMainContext([&]() {
#endif
baton_.reset();
auto funcs = std::move(queue_.wlock()->funcs);
for (auto& func : funcs) {
std::exchange(func, nullptr)();
}
return true;
#if FOLLY_FUTURE_USING_FIBER
});
#endif
}
void detach() {
// Make sure we don't hold the lock while destroying funcs.
[&] {
auto wQueue = queue_.wlock();
wQueue->detached = true;
return std::move(wQueue->funcs);
}();
}
static KeepAlive<WaitExecutor> create() {
return makeKeepAlive<WaitExecutor>(new WaitExecutor());
}
private:
WaitExecutor() {}
bool keepAliveAcquire() override {
auto keepAliveCount =
keepAliveCount_.fetch_add(1, std::memory_order_relaxed);
DCHECK(keepAliveCount > 0);
return true;
}
void keepAliveRelease() override {
auto keepAliveCount =
keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);
DCHECK(keepAliveCount > 0);
if (keepAliveCount == 1) {
delete this;
}
}
struct Queue {
std::vector<Func> funcs;
bool detached{false};
};
folly::Synchronized<Queue> queue_;
FutureBatonType baton_;
std::atomic<ssize_t> keepAliveCount_{1};
};
// Vector-like structure to play with window,
// which otherwise expects a vector of size `times`,
// which would be expensive with large `times` sizes.
struct WindowFakeVector {
using iterator = std::vector<size_t>::iterator;
WindowFakeVector(size_t size) : size_(size) {}
size_t operator[](const size_t index) const {
return index;
}
size_t size() const {
return size_;
}
private:
size_t size_;
};
} // namespace detail
} // namespace futures
template <class T>
SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) {
return makeSemiFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
}
// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
template <class F>
typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F>>::value,
SemiFuture<typename invoke_result_t<F>::value_type>>::type
makeSemiFutureWith(F&& func) {
using InnerType = typename isFutureOrSemiFuture<invoke_result_t<F>>::Inner;
try {
return std::forward<F>(func)();
} catch (std::exception& e) {
return makeSemiFuture<InnerType>(
exception_wrapper(std::current_exception(), e));
} catch (...) {
return makeSemiFuture<InnerType>(
exception_wrapper(std::current_exception()));
}
}
// makeSemiFutureWith(T()) -> SemiFuture<T>
// makeSemiFutureWith(void()) -> SemiFuture<Unit>
template <class F>
typename std::enable_if<
!(isFutureOrSemiFuture<invoke_result_t<F>>::value),
SemiFuture<lift_unit_t<invoke_result_t<F>>>>::type
makeSemiFutureWith(F&& func) {
using LiftedResult = lift_unit_t<invoke_result_t<F>>;
return makeSemiFuture<LiftedResult>(
makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
}
template <class T>
SemiFuture<T> makeSemiFuture(std::exception_ptr const& e) {
return makeSemiFuture(Try<T>(e));
}
template <class T>
SemiFuture<T> makeSemiFuture(exception_wrapper ew) {
return makeSemiFuture(Try<T>(std::move(ew)));
}
template <class T, class E>
typename std::
enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
makeSemiFuture(E const& e) {
return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e)));
}
template <class T>
SemiFuture<T> makeSemiFuture(Try<T> t) {
return SemiFuture<T>(SemiFuture<T>::Core::make(std::move(t)));
}
// This must be defined after the constructors to avoid a bug in MSVC
// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244
inline SemiFuture<Unit> makeSemiFuture() {
return makeSemiFuture(Unit{});
}
template <class T>
SemiFuture<T> SemiFuture<T>::makeEmpty() {
return SemiFuture<T>(futures::detail::EmptyConstruct{});
}
template <class T>
futures::detail::DeferredWrapper SemiFuture<T>::stealDeferredExecutor() {
return this->getCore().stealDeferredExecutor();
}
template <class T>
void SemiFuture<T>::releaseDeferredExecutor(Core* core) {
if (!core || core->hasCallback()) {
return;
}
if (auto executor = core->stealDeferredExecutor()) {
executor.get()->detach();
}
}
template <class T>
SemiFuture<T>::~SemiFuture() {
releaseDeferredExecutor(this->core_);
}
template <class T>
SemiFuture<T>::SemiFuture(SemiFuture<T>&& other) noexcept
: futures::detail::FutureBase<T>(std::move(other)) {}
template <class T>
SemiFuture<T>::SemiFuture(Future<T>&& other) noexcept
: futures::detail::FutureBase<T>(std::move(other)) {
// SemiFuture should not have an executor on construction
if (this->core_) {
this->setExecutor(futures::detail::KeepAliveOrDeferred{});
}
}
template <class T>
SemiFuture<T>& SemiFuture<T>::operator=(SemiFuture<T>&& other) noexcept {
releaseDeferredExecutor(this->core_);
this->assign(std::move(other));
return *this;
}
template <class T>
SemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept {
releaseDeferredExecutor(this->core_);
this->assign(std::move(other));
// SemiFuture should not have an executor on construction
if (this->core_) {
this->setExecutor(Executor::KeepAlive<>{});
}
return *this;
}
template <class T>
Future<T> SemiFuture<T>::via(Executor::KeepAlive<> executor) && {
folly::async_tracing::logSemiFutureVia(this->getExecutor(), executor.get());
if (!executor) {
throw_exception<FutureNoExecutor>();
}
if (auto deferredExecutor = this->getDeferredExecutor()) {
deferredExecutor->setExecutor(executor.copy());
}
auto newFuture = Future<T>(this->core_);
this->core_ = nullptr;
newFuture.setExecutor(std::move(executor));
return newFuture;
}
template <class T>
Future<T> SemiFuture<T>::via(
Executor::KeepAlive<> executor,
int8_t priority) && {
return std::move(*this).via(
ExecutorWithPriority::create(std::move(executor), priority));
}
template <class T>
Future<T> SemiFuture<T>::toUnsafeFuture() && {
return std::move(*this).via(&InlineExecutor::instance());
}
template <class T>
template <typename F>
SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>
SemiFuture<T>::defer(F&& func) && {
auto deferredExecutorPtr = this->getDeferredExecutor();
futures::detail::KeepAliveOrDeferred deferredExecutor = [&]() {
if (deferredExecutorPtr) {
return futures::detail::KeepAliveOrDeferred{deferredExecutorPtr->copy()};
} else {
auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred(
futures::detail::DeferredExecutor::create());
this->setExecutor(newDeferredExecutor.copy());
return newDeferredExecutor;
}
}();
auto sf = Future<T>(this->core_).thenTryInline(std::forward<F>(func)).semi();
this->core_ = nullptr;
// Carry deferred executor through chain as constructor from Future will
// nullify it
sf.setExecutor(std::move(deferredExecutor));
return sf;
}
template <class T>
template <typename F>
SemiFuture<
typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
SemiFuture<T>::deferExTry(F&& func) && {
auto deferredExecutorPtr = this->getDeferredExecutor();
futures::detail::DeferredWrapper deferredExecutor = [&]() mutable {
if (deferredExecutorPtr) {
return deferredExecutorPtr->copy();
} else {
auto newDeferredExecutor = futures::detail::DeferredExecutor::create();
this->setExecutor(
futures::detail::KeepAliveOrDeferred{newDeferredExecutor->copy()});
return newDeferredExecutor;
}
}();
auto sf = Future<T>(this->core_)
.thenExTryInline([func = std::forward<F>(func)](
folly::Executor::KeepAlive<>&& keepAlive,
folly::Try<T>&& val) mutable {
return std::forward<F>(func)(
std::move(keepAlive), std::forward<decltype(val)>(val));
})
.semi();
this->core_ = nullptr;
// Carry deferred executor through chain as constructor from Future will
// nullify it
sf.setExecutor(
futures::detail::KeepAliveOrDeferred{std::move(deferredExecutor)});
return sf;
}
template <class T>
template <typename F>
SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>
SemiFuture<T>::deferValue(F&& func) && {
return std::move(*this).defer(
[f = std::forward<F>(func)](folly::Try<T>&& t) mutable {
return futures::detail::wrapInvoke(std::move(t), std::forward<F>(f));
});
}
template <class T>
template <typename F>
SemiFuture<
typename futures::detail::valueExecutorCallableResult<T, F>::value_type>
SemiFuture<T>::deferExValue(F&& func) && {
return std::move(*this).deferExTry(
[f = std::forward<F>(func)](
folly::Executor::KeepAlive<> ka, folly::Try<T>&& t) mutable {
return std::forward<F>(f)(
ka,
t.template get<
false,
typename futures::detail::valueExecutorCallableResult<T, F>::
ValueArg>());
});
}
template <class T>
template <class ExceptionType, class F>
SemiFuture<T> SemiFuture<T>::deferError(tag_t<ExceptionType>, F&& func) && {
return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T>&& t) mutable {
if (auto e = t.template tryGetExceptionObject<ExceptionType>()) {
return makeSemiFutureWith(
[&]() mutable { return std::forward<F>(func)(*e); });
} else {
return makeSemiFuture<T>(std::move(t));
}
});
}
template <class T>
template <class F>
SemiFuture<T> SemiFuture<T>::deferError(F&& func) && {
return std::move(*this).defer(
[func = std::forward<F>(func)](Try<T> t) mutable {
if (t.hasException()) {
return makeSemiFutureWith([&]() mutable {
return std::forward<F>(func)(std::move(t.exception()));
});
} else {
return makeSemiFuture<T>(std::move(t));
}
});
}
template <class T>
SemiFuture<Unit> SemiFuture<T>::unit() && {
return std::move(*this).deferValue([](T&&) {});
}
template <typename T>
SemiFuture<T> SemiFuture<T>::delayed(HighResDuration dur, Timekeeper* tk) && {
return collectAllSemiFuture(*this, futures::sleep(dur, tk))
.deferValue([](std::tuple<Try<T>, Try<Unit>> tup) {
Try<T>& t = std::get<0>(tup);
return makeFuture<T>(std::move(t));
});
}
template <class T>
Future<T> Future<T>::makeEmpty() {
return Future<T>(futures::detail::EmptyConstruct{});
}
template <class T>
Future<T>::Future(Future<T>&& other) noexcept
: futures::detail::FutureBase<T>(std::move(other)) {}
template <class T>
Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
this->assign(std::move(other));
return *this;
}
// unwrap
template <class T>
template <class F>
typename std::
enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type
Future<T>::unwrap() && {
return std::move(*this).thenValue(
[](Future<typename isFuture<T>::Inner> internal_future) {
return internal_future;
});
}
template <class T>
Future<T> Future<T>::via(Executor::KeepAlive<> executor) && {
folly::async_tracing::logFutureVia(this->getExecutor(), executor.get());
this->setExecutor(std::move(executor));
auto newFuture = Future<T>(this->core_);
this->core_ = nullptr;
return newFuture;
}
template <class T>
Future<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) && {
return std::move(*this).via(
ExecutorWithPriority::create(std::move(executor), priority));
}
template <class T>
Future<T> Future<T>::via(Executor::KeepAlive<> executor) & {
folly::async_tracing::logFutureVia(this->getExecutor(), executor.get());
this->throwIfInvalid();
Promise<T> p;
auto sf = p.getSemiFuture();
auto func = [p = std::move(p)](Executor::KeepAlive<>&&, Try<T>&& t) mutable {
p.setTry(std::move(t));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(func)>;
this->thenImplementation(
std::move(func), R{}, futures::detail::InlineContinuation::forbid);
// Construct future from semifuture manually because this may not have
// an executor set due to legacy code. This means we can bypass the executor
// check in SemiFuture::via
auto f = Future<T>(sf.core_);
sf.core_ = nullptr;
return std::move(f).via(std::move(executor));
}
template <class T>
Future<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) & {
return this->via(ExecutorWithPriority::create(std::move(executor), priority));
}
template <typename T>
template <typename R, typename Caller, typename... Args>
Future<typename isFuture<R>::Inner> Future<T>::then(
R (Caller::*func)(Args...),
Caller* instance) && {
using FirstArg =
remove_cvref_t<typename futures::detail::ArgType<Args...>::FirstArg>;
return std::move(*this).thenTry([instance, func](Try<T>&& t) {
return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
});
}
template <class T>
template <typename F>
Future<typename futures::detail::tryCallableResult<T, F>::value_type>
Future<T>::thenTry(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
folly::Executor::KeepAlive<>&&,
folly::Try<T>&& t) mutable {
return std::forward<F>(f)(std::move(t));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid);
}
template <class T>
template <typename F>
Future<typename futures::detail::tryCallableResult<T, F>::value_type>
Future<T>::thenTryInline(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
folly::Executor::KeepAlive<>&&,
folly::Try<T>&& t) mutable {
return std::forward<F>(f)(std::move(t));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit);
}
template <class T>
template <typename F>
Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
Future<T>::thenExTry(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {
// Enforce that executor cannot be null
DCHECK(ka);
return std::forward<F>(f)(std::move(ka), std::move(t));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid);
}
template <class T>
template <typename F>
Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>
Future<T>::thenExTryInline(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {
// Enforce that executor cannot be null
DCHECK(ka);
return std::forward<F>(f)(std::move(ka), std::move(t));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit);
}
template <class T>
template <typename F>
Future<typename futures::detail::valueCallableResult<T, F>::value_type>
Future<T>::thenValue(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable {
return futures::detail::wrapInvoke(std::move(t), std::forward<F>(f));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid);
}
template <class T>
template <typename F>
Future<typename futures::detail::valueCallableResult<T, F>::value_type>
Future<T>::thenValueInline(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable {
return futures::detail::wrapInvoke(std::move(t), std::forward<F>(f));
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit);
}
template <class T>
template <typename F>
Future<typename futures::detail::valueExecutorCallableResult<T, F>::value_type>
Future<T>::thenExValue(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {
// Enforce that executor cannot be null
DCHECK(ka);
return std::forward<F>(f)(
std::move(ka),
t.template get<
false,
typename futures::detail::valueExecutorCallableResult<T, F>::
ValueArg>());
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::forbid);
}
template <class T>
template <typename F>
Future<typename futures::detail::valueExecutorCallableResult<T, F>::value_type>
Future<T>::thenExValueInline(F&& func) && {
auto lambdaFunc = [f = std::forward<F>(func)](
Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {
// Enforce that executor cannot be null
DCHECK(ka);
return std::forward<F>(f)(
std::move(ka),
t.template get<
false,
typename futures::detail::valueExecutorCallableResult<T, F>::
ValueArg>());
};
using R = futures::detail::tryExecutorCallableResult<T, decltype(lambdaFunc)>;
return this->thenImplementation(
std::move(lambdaFunc), R{}, futures::detail::InlineContinuation::permit);
}
template <class T>
template <class ExceptionType, class F>
typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type
Future<T>::thenError(tag_t<ExceptionType>, F&& func) && {
Promise<T> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
auto sf = p.getSemiFuture();
auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
this->setCallback_([state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
if (auto ex = t.template tryGetExceptionObject<
std::remove_reference_t<ExceptionType>>()) {
auto tf2 = state.tryInvoke(std::move(*ex));
if (tf2.hasException()) {
state.setException(std::move(ka), std::move(tf2.exception()));
} else {
tf2->setCallback_(
[p = state.stealPromise()](
Executor::KeepAlive<>&& innerKA, Try<T>&& t3) mutable {
p.setTry(std::move(innerKA), std::move(t3));
});
}
} else {
state.setTry(std::move(ka), std::move(t));
}
});
return std::move(sf).via(std::move(e));
}
template <class T>
template <class ExceptionType, class F>
typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type
Future<T>::thenError(tag_t<ExceptionType>, F&& func) && {
Promise<T> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
auto sf = p.getSemiFuture();
auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
this->setCallback_([state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
if (auto ex = t.template tryGetExceptionObject<
std::remove_reference_t<ExceptionType>>()) {
state.setTry(std::move(ka), makeTryWith([&] {
return state.invoke(std::move(*ex));
}));
} else {
state.setTry(std::move(ka), std::move(t));
}
});
return std::move(sf).via(std::move(e));
}
template <class T>
template <class F>
typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
Future<T>>::type
Future<T>::thenError(F&& func) && {
auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
Promise<T> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
auto sf = p.getSemiFuture();
this->setCallback_([state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Executor::KeepAlive<>&& ka, Try<T> t) mutable {
if (t.hasException()) {
auto tf2 = state.tryInvoke(std::move(t.exception()));
if (tf2.hasException()) {
state.setException(std::move(ka), std::move(tf2.exception()));
} else {
tf2->setCallback_(
[p = state.stealPromise()](
Executor::KeepAlive<>&& innerKA, Try<T>&& t3) mutable {
p.setTry(std::move(innerKA), std::move(t3));
});
}
} else {
state.setTry(std::move(ka), std::move(t));
}
});
return std::move(sf).via(std::move(e));
}
template <class T>
template <class F>
typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
Future<T>>::type
Future<T>::thenError(F&& func) && {
auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
Promise<T> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
auto sf = p.getSemiFuture();
this->setCallback_([state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {
if (t.hasException()) {
state.setTry(std::move(ka), makeTryWith([&] {
return state.invoke(std::move(t.exception()));
}));
} else {
state.setTry(std::move(ka), std::move(t));
}
});
return std::move(sf).via(std::move(e));
}
template <class T>
Future<Unit> Future<T>::then() && {
return std::move(*this).thenValue([](T&&) {});
}
template <class T>
template <class F>
Future<T> Future<T>::ensure(F&& func) && {
return std::move(*this).thenTry(
[funcw = std::forward<F>(func)](Try<T>&& t) mutable {
std::forward<F>(funcw)();
return makeFuture(std::move(t));
});
}
template <class T>
template <class F>
Future<T>
Future<T>::onTimeout(HighResDuration dur, F&& func, Timekeeper* tk) && {
return std::move(*this).within(dur, tk).thenError(
tag_t<FutureTimeout>{},
[funcw = std::forward<F>(func)](auto const&) mutable {
return std::forward<F>(funcw)();
});
}
template <class Func>
auto via(Executor::KeepAlive<> x, Func&& func) -> Future<
typename isFutureOrSemiFuture<decltype(std::declval<Func>()())>::Inner> {
return via(std::move(x))
.thenValue([f = std::forward<Func>(func)](auto&&) mutable {
return std::forward<Func>(f)();
});
}
// makeFuture
template <class T>
Future<typename std::decay<T>::type> makeFuture(T&& t) {
return makeFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
}
inline Future<Unit> makeFuture() {
return makeFuture(Unit{});
}
// makeFutureWith(Future<T>()) -> Future<T>
template <class F>
typename std::
enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type
makeFutureWith(F&& func) {
using InnerType = typename isFuture<invoke_result_t<F>>::Inner;
try {
return std::forward<F>(func)();
} catch (std::exception& e) {
return makeFuture<InnerType>(
exception_wrapper(std::current_exception(), e));
} catch (...) {
return makeFuture<InnerType>(exception_wrapper(std::current_exception()));
}
}
// makeFutureWith(T()) -> Future<T>
// makeFutureWith(void()) -> Future<Unit>
template <class F>
typename std::enable_if<
!(isFuture<invoke_result_t<F>>::value),
Future<lift_unit_t<invoke_result_t<F>>>>::type
makeFutureWith(F&& func) {
using LiftedResult = lift_unit_t<invoke_result_t<F>>;
return makeFuture<LiftedResult>(
makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
}
template <class T>
Future<T> makeFuture(std::exception_ptr const& e) {
return makeFuture(Try<T>(e));
}
template <class T>
Future<T> makeFuture(exception_wrapper ew) {
return makeFuture(Try<T>(std::move(ew)));
}
template <class T, class E>
typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>::
type
makeFuture(E const& e) {
return makeFuture(Try<T>(make_exception_wrapper<E>(e)));
}
template <class T>
Future<T> makeFuture(Try<T> t) {
return Future<T>(Future<T>::Core::make(std::move(t)));
}
// via
Future<Unit> via(Executor::KeepAlive<> executor) {
return makeFuture().via(std::move(executor));
}
Future<Unit> via(Executor::KeepAlive<> executor, int8_t priority) {
return makeFuture().via(std::move(executor), priority);
}
namespace futures {
namespace detail {
template <typename V, typename... Fs, std::size_t... Is>
FOLLY_ERASE void foreach_(std::index_sequence<Is...>, V&& v, Fs&&... fs) {
using _ = int[];
void(_{0, (void(v(index_constant<Is>{}, static_cast<Fs&&>(fs))), 0)...});
}
template <typename V, typename... Fs>
FOLLY_ERASE void foreach(V&& v, Fs&&... fs) {
using _ = std::index_sequence_for<Fs...>;
foreach_(_{}, static_cast<V&&>(v), static_cast<Fs&&>(fs)...);
}
template <typename T>
futures::detail::DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) {
return future.getDeferredExecutor();
}
template <typename T>
futures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future) {
return future.stealDeferredExecutor();
}
template <typename T>
futures::detail::DeferredWrapper stealDeferredExecutor(Future<T>&) {
return {};
}
template <typename... Ts>
void stealDeferredExecutorsVariadic(
std::vector<futures::detail::DeferredWrapper>& executors,
Ts&... ts) {
foreach(
[&](auto, auto& future) {
if (auto executor = stealDeferredExecutor(future)) {
executors.push_back(std::move(executor));
}
},
ts...);
}
template <class InputIterator>
void stealDeferredExecutors(
std::vector<futures::detail::DeferredWrapper>& executors,
InputIterator first,
InputIterator last) {
for (auto it = first; it != last; ++it) {
if (auto executor = stealDeferredExecutor(*it)) {
executors.push_back(std::move(executor));
}
}
}
} // namespace detail
} // namespace futures
// collectAll (variadic)
template <typename... Fs>
SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
collectAll(Fs&&... fs) {
using Result = std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>;
struct Context {
~Context() {
p.setValue(std::move(results));
}
Promise<Result> p;
Result results;
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutorsVariadic(executors, fs...);
auto ctx = std::make_shared<Context>();
futures::detail::foreach(
[&](auto i, auto&& f) {
f.setCallback_([i, ctx](auto&&, auto&& t) {
std::get<i.value>(ctx->results) = std::move(t);
});
},
static_cast<Fs&&>(fs)...);
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
auto work = [](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
};
future = std::move(future).defer(work);
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
template <typename... Fs>
SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
collectAllSemiFuture(Fs&&... fs) {
return collectAll(std::forward<Fs>(fs)...);
}
template <typename... Fs>
Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
collectAllUnsafe(Fs&&... fs) {
return collectAllSemiFuture(std::forward<Fs>(fs)...).toUnsafeFuture();
}
// collectAll (iterator)
template <class InputIterator>
SemiFuture<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAll(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type;
struct Context {
explicit Context(size_t n) : results(n), count(n) {}
~Context() {
futures::detail::setTry(
p, std::move(ka), Try<std::vector<Try<T>>>(std::move(results)));
}
Promise<std::vector<Try<T>>> p;
Executor::KeepAlive<> ka;
std::vector<Try<T>> results;
std::atomic<size_t> count;
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));
for (size_t i = 0; first != last; ++first, ++i) {
first->setCallback_(
[i, ctx](Executor::KeepAlive<>&& ka, Try<T>&& t) {
ctx->results[i] = std::move(t);
if (ctx->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
ctx->ka = std::move(ka);
}
},
futures::detail::InlineContinuation::permit);
}
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
future = std::move(future).defer(
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
auto deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
template <class InputIterator>
Future<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAllUnsafe(InputIterator first, InputIterator last) {
return collectAll(first, last).toUnsafeFuture();
}
template <class InputIterator>
SemiFuture<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAllSemiFuture(InputIterator first, InputIterator last) {
return collectAll(first, last);
}
// collect (iterator)
template <class InputIterator>
SemiFuture<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>>
collect(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type;
struct Context {
explicit Context(size_t n) : result(n) {
finalResult.reserve(n);
}
~Context() {
if (!threw.load(std::memory_order_relaxed)) {
// map Optional<T> -> T
std::transform(
result.begin(),
result.end(),
std::back_inserter(finalResult),
[](Optional<T>& o) { return std::move(o.value()); });
p.setValue(std::move(finalResult));
}
}
Promise<std::vector<T>> p;
std::vector<Optional<T>> result;
std::vector<T> finalResult;
std::atomic<bool> threw{false};
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(std::distance(first, last));
for (size_t i = 0; first != last; ++first, ++i) {
first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {
if (t.hasException()) {
if (!ctx->threw.exchange(true, std::memory_order_relaxed)) {
ctx->p.setException(std::move(t.exception()));
}
} else if (!ctx->threw.load(std::memory_order_relaxed)) {
ctx->result[i] = std::move(t.value());
}
});
}
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
auto work = [](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
};
future = std::move(future).defer(work);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
template <class InputIterator>
Future<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>>
collectUnsafe(InputIterator first, InputIterator last) {
return collect(first, last).toUnsafeFuture();
}
template <class InputIterator>
SemiFuture<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>>
collectSemiFuture(InputIterator first, InputIterator last) {
return collect(first, last);
}
// collect (variadic)
template <typename... Fs>
SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(
Fs&&... fs) {
using Result = std::tuple<typename remove_cvref_t<Fs>::value_type...>;
struct Context {
~Context() {
if (!threw.load(std::memory_order_relaxed)) {
p.setValue(unwrapTryTuple(std::move(results)));
}
}
Promise<Result> p;
std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...> results;
std::atomic<bool> threw{false};
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutorsVariadic(executors, fs...);
auto ctx = std::make_shared<Context>();
futures::detail::foreach(
[&](auto i, auto&& f) {
f.setCallback_([i, ctx](Executor::KeepAlive<>&&, auto&& t) {
if (t.hasException()) {
if (!ctx->threw.exchange(true, std::memory_order_relaxed)) {
ctx->p.setException(std::move(t.exception()));
}
} else if (!ctx->threw.load(std::memory_order_relaxed)) {
std::get<i.value>(ctx->results) = std::move(t);
}
});
},
static_cast<Fs&&>(fs)...);
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
auto work = [](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
};
future = std::move(future).defer(work);
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
template <typename... Fs>
SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>>
collectSemiFuture(Fs&&... fs) {
return collect(std::forward<Fs>(fs)...);
}
template <typename... Fs>
Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collectUnsafe(
Fs&&... fs) {
return collect(std::forward<Fs>(fs)...).toUnsafeFuture();
}
template <class Collection>
auto collectSemiFuture(Collection&& c)
-> decltype(collectSemiFuture(c.begin(), c.end())) {
return collectSemiFuture(c.begin(), c.end());
}
// collectAny (iterator)
// TODO(T26439406): Make return SemiFuture
template <class InputIterator>
SemiFuture<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAnySemiFuture(InputIterator first, InputIterator last) {
return collectAny(first, last);
}
template <class InputIterator>
Future<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAnyUnsafe(InputIterator first, InputIterator last) {
return collectAny(first, last).toUnsafeFuture();
}
template <class InputIterator>
SemiFuture<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAny(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type;
struct Context {
Promise<std::pair<size_t, Try<T>>> p;
std::atomic<bool> done{false};
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>();
for (size_t i = 0; first != last; ++first, ++i) {
first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {
if (!ctx->done.exchange(true, std::memory_order_relaxed)) {
ctx->p.setValue(std::make_pair(i, std::move(t)));
}
});
}
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
future = std::move(future).defer(
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
// collectAnyWithoutException (iterator)
template <class InputIterator>
SemiFuture<std::pair<
size_t,
typename std::iterator_traits<InputIterator>::value_type::value_type>>
collectAnyWithoutException(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type;
struct Context {
Context(size_t n) : nTotal(n) {}
Promise<std::pair<size_t, T>> p;
std::atomic<bool> done{false};
std::atomic<size_t> nFulfilled{0};
size_t nTotal;
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));
for (size_t i = 0; first != last; ++first, ++i) {
first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {
if (!t.hasException() &&
!ctx->done.exchange(true, std::memory_order_relaxed)) {
ctx->p.setValue(std::make_pair(i, std::move(t.value())));
} else if (
ctx->nFulfilled.fetch_add(1, std::memory_order_relaxed) + 1 ==
ctx->nTotal) {
ctx->p.setException(t.exception());
}
});
}
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
future = std::move(future).defer(
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
// collectN (iterator)
template <class InputIterator>
SemiFuture<std::vector<std::pair<
size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
collectN(InputIterator first, InputIterator last, size_t n) {
using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type;
using Result = std::vector<std::pair<size_t, Try<T>>>;
struct Context {
explicit Context(size_t numFutures, size_t min_)
: v(numFutures), min(min_) {}
std::vector<Optional<Try<T>>> v;
size_t min;
std::atomic<size_t> completed = {0}; // # input futures completed
std::atomic<size_t> stored = {0}; // # output values stored
Promise<Result> p;
};
assert(n > 0);
assert(std::distance(first, last) >= 0);
if (size_t(std::distance(first, last)) < n) {
return SemiFuture<Result>(
exception_wrapper(std::runtime_error("Not enough futures")));
}
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
// for each completed Future, increase count and add to vector, until we
// have n completed futures at which point we fulfil our Promise with the
// vector
auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)), n);
for (size_t i = 0; first != last; ++first, ++i) {
first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {
// relaxed because this guards control but does not guard data
auto const c = 1 + ctx->completed.fetch_add(1, std::memory_order_relaxed);
if (c > ctx->min) {
return;
}
ctx->v[i] = std::move(t);
// release because the stored values in all threads must be visible below
// acquire because no stored value is permitted to be fetched early
auto const s = 1 + ctx->stored.fetch_add(1, std::memory_order_acq_rel);
if (s < ctx->min) {
return;
}
Result result;
result.reserve(ctx->completed.load());
for (size_t j = 0; j < ctx->v.size(); ++j) {
auto& entry = ctx->v[j];
if (entry.hasValue()) {
result.emplace_back(j, std::move(entry).value());
}
}
ctx->p.setTry(Try<Result>(std::move(result)));
});
}
auto future = ctx->p.getSemiFuture();
if (!executors.empty()) {
future = std::move(future).defer(
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
// reduce (iterator)
template <class It, class T, class F>
Future<T> reduce(It first, It last, T&& initial, F&& func) {
if (first == last) {
return makeFuture(std::forward<T>(initial));
}
typedef typename std::iterator_traits<It>::value_type::value_type ItT;
typedef typename std::
conditional<is_invocable<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type
Arg;
typedef isTry<Arg> IsTry;
auto sfunc = std::make_shared<std::decay_t<F>>(std::forward<F>(func));
auto f = std::move(*first).thenTry(
[initial = std::forward<T>(initial), sfunc](Try<ItT>&& head) mutable {
return (*sfunc)(
std::move(initial), head.template get<IsTry::value, Arg&&>());
});
for (++first; first != last; ++first) {
f = collectAllSemiFuture(f, *first).toUnsafeFuture().thenValue(
[sfunc](std::tuple<Try<T>, Try<ItT>>&& t) {
return (*sfunc)(
std::move(std::get<0>(t).value()),
// Either return a ItT&& or a Try<ItT>&& depending
// on the type of the argument of func.
std::get<1>(t).template get<IsTry::value, Arg&&>());
});
}
return f;
}
// window (collection)
template <class Collection, class F, class ItT, class Result>
std::vector<Future<Result>> window(Collection input, F func, size_t n) {
// Use global QueuedImmediateExecutor singleton to avoid stack overflow.
auto executor = &QueuedImmediateExecutor::instance();
return window(executor, std::move(input), std::move(func), n);
}
template <class F>
auto window(size_t times, F func, size_t n)
-> std::vector<invoke_result_t<F, size_t>> {
return window(futures::detail::WindowFakeVector(times), std::move(func), n);
}
template <class Collection, class F, class ItT, class Result>
std::vector<Future<Result>>
window(Executor::KeepAlive<> executor, Collection input, F func, size_t n) {
struct WindowContext {
WindowContext(
Executor::KeepAlive<> executor_,
Collection&& input_,
F&& func_)
: executor(std::move(executor_)),
input(std::move(input_)),
promises(input.size()),
func(std::move(func_)) {}
std::atomic<size_t> i{0};
Executor::KeepAlive<> executor;
Collection input;
std::vector<Promise<Result>> promises;
F func;
static void spawn(std::shared_ptr<WindowContext> ctx) {
size_t i = ctx->i.fetch_add(1, std::memory_order_relaxed);
if (i < ctx->input.size()) {
auto fut = makeSemiFutureWith(
[&] { return ctx->func(std::move(ctx->input[i])); })
.via(ctx->executor.get());
fut.setCallback_([ctx = std::move(ctx), i](
Executor::KeepAlive<>&&, Try<Result>&& t) mutable {
ctx->promises[i].setTry(std::move(t));
// Chain another future onto this one
spawn(std::move(ctx));
});
}
}
};
auto max = std::min(n, input.size());
auto ctx = std::make_shared<WindowContext>(
executor.copy(), std::move(input), std::move(func));
// Start the first n Futures
for (size_t i = 0; i < max; ++i) {
executor->add([ctx]() mutable { WindowContext::spawn(std::move(ctx)); });
}
std::vector<Future<Result>> futures;
futures.reserve(ctx->promises.size());
for (auto& promise : ctx->promises) {
futures.emplace_back(promise.getSemiFuture().via(executor.copy()));
}
return futures;
}
// reduce
template <class T>
template <class I, class F>
Future<I> Future<T>::reduce(I&& initial, F&& func) && {
return std::move(*this).thenValue(
[minitial = std::forward<I>(initial),
mfunc = std::forward<F>(func)](T&& vals) mutable {
auto ret = std::move(minitial);
for (auto& val : vals) {
ret = mfunc(std::move(ret), std::move(val));
}
return ret;
});
}
// unorderedReduce (iterator)
template <class It, class T, class F>
SemiFuture<T> unorderedReduceSemiFuture(It first, It last, T initial, F func) {
using ItF = typename std::iterator_traits<It>::value_type;
using ItT = typename ItF::value_type;
using Arg = MaybeTryArg<F, T, ItT>;
if (first == last) {
return makeFuture(std::move(initial));
}
typedef isTry<Arg> IsTry;
struct Context {
Context(T&& memo, F&& fn, size_t n)
: lock_(),
memo_(makeFuture<T>(std::move(memo))),
func_(std::move(fn)),
numThens_(0),
numFutures_(n),
promise_() {}
folly::MicroSpinLock lock_; // protects memo_ and numThens_
Future<T> memo_;
F func_;
size_t numThens_; // how many Futures completed and called .then()
size_t numFutures_; // how many Futures in total
Promise<T> promise_;
};
struct Fulfill {
void operator()(Promise<T>&& p, T&& v) const {
p.setValue(std::move(v));
}
void operator()(Promise<T>&& p, Future<T>&& f) const {
f.setCallback_(
[p = std::move(p)](Executor::KeepAlive<>&&, Try<T>&& t) mutable {
p.setTry(std::move(t));
});
}
};
std::vector<futures::detail::DeferredWrapper> executors;
futures::detail::stealDeferredExecutors(executors, first, last);
auto ctx = std::make_shared<Context>(
std::move(initial), std::move(func), std::distance(first, last));
for (size_t i = 0; first != last; ++first, ++i) {
first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<ItT>&& t) {
(void)i;
// Futures can be completed in any order, simultaneously.
// To make this non-blocking, we create a new Future chain in
// the order of completion to reduce the values.
// The spinlock just protects chaining a new Future, not actually
// executing the reduce, which should be really fast.
Promise<T> p;
auto f = p.getFuture();
{
folly::MSLGuard lock(ctx->lock_);
f = std::exchange(ctx->memo_, std::move(f));
if (++ctx->numThens_ == ctx->numFutures_) {
// After reducing the value of the last Future, fulfill the Promise
ctx->memo_.setCallback_([ctx](Executor::KeepAlive<>&&, Try<T>&& t2) {
ctx->promise_.setValue(std::move(t2));
});
}
}
f.setCallback_([ctx, mp = std::move(p), mt = std::move(t)](
Executor::KeepAlive<>&&, Try<T>&& v) mutable {
if (v.hasValue()) {
try {
Fulfill{}(
std::move(mp),
ctx->func_(
std::move(v.value()),
mt.template get<IsTry::value, Arg&&>()));
} catch (std::exception& e) {
mp.setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
mp.setException(exception_wrapper(std::current_exception()));
}
} else {
mp.setTry(std::move(v));
}
});
});
}
auto future = ctx->promise_.getSemiFuture();
if (!executors.empty()) {
future = std::move(future).defer(
[](Try<typename decltype(future)::value_type>&& t) {
return std::move(t).value();
});
const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);
deferredExecutor->setNestedExecutors(std::move(executors));
}
return future;
}
template <class It, class T, class F>
Future<T> unorderedReduce(It first, It last, T initial, F func) {
return unorderedReduceSemiFuture(
first, last, std::move(initial), std::move(func))
.via(&InlineExecutor::instance());
}
// within
template <class T>
Future<T> Future<T>::within(HighResDuration dur, Timekeeper* tk) && {
return std::move(*this).within(dur, FutureTimeout(), tk);
}
template <class T>
template <class E>
Future<T> Future<T>::within(HighResDuration dur, E e, Timekeeper* tk) && {
if (this->isReady()) {
return std::move(*this);
}
auto* ePtr = this->getExecutor();
auto exe =
folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
return std::move(*this).semi().within(dur, e, tk).via(std::move(exe));
}
template <class T>
template <typename E>
SemiFuture<T>
SemiFuture<T>::within(HighResDuration dur, E e, Timekeeper* tk) && {
if (this->isReady()) {
return std::move(*this);
}
struct Context {
explicit Context(E ex) : exception(std::move(ex)) {}
E exception;
SemiFuture<Unit> thisFuture;
SemiFuture<Unit> afterFuture;
Promise<T> promise;
std::atomic<bool> token{false};
};
std::shared_ptr<Timekeeper> tks;
if (LIKELY(!tk)) {
tks = folly::detail::getTimekeeperSingleton();
tk = tks.get();
}
if (UNLIKELY(!tk)) {
return makeSemiFuture<T>(FutureNoTimekeeper());
}
auto ctx = std::make_shared<Context>(std::move(e));
ctx->thisFuture = std::move(*this).defer([ctx](Try<T>&& t) {
if (!ctx->token.exchange(true, std::memory_order_relaxed)) {
ctx->promise.setTry(std::move(t));
ctx->afterFuture.cancel();
}
});
// Have time keeper use a weak ptr to hold ctx,
// so that ctx can be deallocated as soon as the future job finished.
ctx->afterFuture =
tk->after(dur).defer([weakCtx = to_weak_ptr(ctx)](Try<Unit>&& t) mutable {
if (t.hasException() &&
t.exception().is_compatible_with<FutureCancellation>()) {
// This got cancelled by thisFuture so we can just return.
return;
}
auto lockedCtx = weakCtx.lock();
if (!lockedCtx) {
// ctx already released. "this" completed first, cancel "after"
return;
}
// "after" completed first, cancel "this"
lockedCtx->thisFuture.raise(FutureTimeout());
if (!lockedCtx->token.exchange(true, std::memory_order_relaxed)) {
if (t.hasException()) {
lockedCtx->promise.setException(std::move(t.exception()));
} else {
lockedCtx->promise.setException(std::move(lockedCtx->exception));
}
}
});
// Properly propagate interrupt values through futures chained after within()
ctx->promise.setInterruptHandler(
[weakCtx = to_weak_ptr(ctx)](const exception_wrapper& ex) {
if (auto lockedCtx = weakCtx.lock()) {
lockedCtx->thisFuture.raise(ex);
}
});
// Construct the future to return, create a fresh DeferredExecutor and
// nest the other two inside it, in case they already carry nested executors.
auto fut = ctx->promise.getSemiFuture();
auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred(
futures::detail::DeferredExecutor::create());
fut.setExecutor(std::move(newDeferredExecutor));
std::vector<folly::futures::detail::DeferredWrapper> nestedExecutors;
nestedExecutors.emplace_back(ctx->thisFuture.stealDeferredExecutor());
nestedExecutors.emplace_back(ctx->afterFuture.stealDeferredExecutor());
futures::detail::getDeferredExecutor(fut)->setNestedExecutors(
std::move(nestedExecutors));
return fut;
}
// delayed
template <class T>
Future<T> Future<T>::delayed(HighResDuration dur, Timekeeper* tk) && {
auto e = this->getExecutor();
return collectAllSemiFuture(*this, futures::sleep(dur, tk))
.via(e ? e : &InlineExecutor::instance())
.thenValue([](std::tuple<Try<T>, Try<Unit>>&& tup) {
return makeFuture<T>(std::get<0>(std::move(tup)));
});
}
namespace futures {
namespace detail {
template <class FutureType, typename T = typename FutureType::value_type>
void waitImpl(FutureType& f) {
if (std::is_base_of<Future<T>, FutureType>::value) {
f = std::move(f).via(&InlineExecutor::instance());
}
// short-circuit if there's nothing to do
if (f.isReady()) {
return;
}
Promise<T> promise;
auto ret = convertFuture(promise.getSemiFuture(), f);
FutureBatonType baton;
f.setCallback_([&baton, promise = std::move(promise)](
Executor::KeepAlive<>&&, Try<T>&& t) mutable {
promise.setTry(std::move(t));
baton.post();
});
f = std::move(ret);
baton.wait();
assert(f.isReady());
}
template <class T>
Future<T> convertFuture(SemiFuture<T>&& sf, const Future<T>& f) {
// Carry executor from f, inserting an inline executor if it did not have one
auto* exe = f.getExecutor();
auto newFut = std::move(sf).via(exe ? exe : &InlineExecutor::instance());
newFut.core_->setInterruptHandlerNoLock(f.core_->getInterruptHandler());
return newFut;
}
template <class T>
SemiFuture<T> convertFuture(SemiFuture<T>&& sf, const SemiFuture<T>&) {
return std::move(sf);
}
template <class FutureType, typename T = typename FutureType::value_type>
void waitImpl(FutureType& f, HighResDuration dur) {
if (std::is_base_of<Future<T>, FutureType>::value) {
f = std::move(f).via(&InlineExecutor::instance());
}
// short-circuit if there's nothing to do
if (f.isReady()) {
return;
}
Promise<T> promise;
auto ret = convertFuture(promise.getSemiFuture(), f);
auto baton = std::make_shared<FutureBatonType>();
f.setCallback_([baton, promise = std::move(promise)](
Executor::KeepAlive<>&&, Try<T>&& t) mutable {
promise.setTry(std::move(t));
baton->post();
});
f = std::move(ret);
if (baton->try_wait_for(dur)) {
assert(f.isReady());
}
}
template <class T>
void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
// Set callback so to ensure that the via executor has something on it
// so that once the preceding future triggers this callback, drive will
// always have a callback to satisfy it
if (f.isReady()) {
return;
}
f = std::move(f).via(e).thenTry([](Try<T>&& t) { return std::move(t); });
while (!f.isReady()) {
e->drive();
}
assert(f.isReady());
f = std::move(f).via(&InlineExecutor::instance());
}
template <class T, typename Rep, typename Period>
void waitViaImpl(
Future<T>& f,
TimedDrivableExecutor* e,
const std::chrono::duration<Rep, Period>& timeout) {
// Set callback so to ensure that the via executor has something on it
// so that once the preceding future triggers this callback, drive will
// always have a callback to satisfy it
if (f.isReady()) {
return;
}
// Chain operations, ensuring that the executor is kept alive for the duration
f = std::move(f).via(e).thenValue(
[keepAlive = getKeepAliveToken(e)](T&& t) { return std::move(t); });
auto now = std::chrono::steady_clock::now();
auto deadline = now + timeout;
while (!f.isReady() && (now < deadline)) {
e->try_drive_until(deadline);
now = std::chrono::steady_clock::now();
}
assert(f.isReady() || (now >= deadline));
if (f.isReady()) {
f = std::move(f).via(&InlineExecutor::instance());
}
}
} // namespace detail
} // namespace futures
template <class T>
SemiFuture<T>& SemiFuture<T>::wait() & {
if (auto deferredExecutor = this->getDeferredExecutor()) {
// Make sure that the last callback in the future chain will be run on the
// WaitExecutor.
Promise<T> promise;
auto ret = promise.getSemiFuture();
setCallback_(
[p = std::move(promise)](Executor::KeepAlive<>&&, auto&& r) mutable {
p.setTry(std::move(r));
});
auto waitExecutor = futures::detail::WaitExecutor::create();
deferredExecutor->setExecutor(waitExecutor.copy());
while (!ret.isReady()) {
waitExecutor->drive();
}
waitExecutor->detach();
this->detach();
*this = std::move(ret);
} else {
futures::detail::waitImpl(*this);
}
return *this;
}
template <class T>
SemiFuture<T>&& SemiFuture<T>::wait() && {
return std::move(wait());
}
template <class T>
SemiFuture<T>& SemiFuture<T>::wait(HighResDuration dur) & {
if (auto deferredExecutor = this->getDeferredExecutor()) {
// Make sure that the last callback in the future chain will be run on the
// WaitExecutor.
Promise<T> promise;
auto ret = promise.getSemiFuture();
setCallback_(
[p = std::move(promise)](Executor::KeepAlive<>&&, auto&& r) mutable {
p.setTry(std::move(r));
});
auto waitExecutor = futures::detail::WaitExecutor::create();
auto deadline = futures::detail::WaitExecutor::Clock::now() + dur;
deferredExecutor->setExecutor(waitExecutor.copy());
while (!ret.isReady()) {
if (!waitExecutor->driveUntil(deadline)) {
break;
}
}
waitExecutor->detach();
this->detach();
*this = std::move(ret);
} else {
futures::detail::waitImpl(*this, dur);
}
return *this;
}
template <class T>
bool SemiFuture<T>::wait(HighResDuration dur) && {
auto future = std::move(*this);
future.wait(dur);
return future.isReady();
}
template <class T>
T SemiFuture<T>::get() && {
return std::move(*this).getTry().value();
}
template <class T>
T SemiFuture<T>::get(HighResDuration dur) && {
return std::move(*this).getTry(dur).value();
}
template <class T>
Try<T> SemiFuture<T>::getTry() && {
wait();
auto future = folly::Future<T>(this->core_);
this->core_ = nullptr;
return std::move(std::move(future).getTry());
}
template <class T>
Try<T> SemiFuture<T>::getTry(HighResDuration dur) && {
wait(dur);
auto future = folly::Future<T>(this->core_);
this->core_ = nullptr;
if (!future.isReady()) {
throw_exception<FutureTimeout>();
}
return std::move(std::move(future).getTry());
}
template <class T>
Future<T>& Future<T>::wait() & {
futures::detail::waitImpl(*this);
return *this;
}
template <class T>
Future<T>&& Future<T>::wait() && {
futures::detail::waitImpl(*this);
return std::move(*this);
}
template <class T>
Future<T>& Future<T>::wait(HighResDuration dur) & {
futures::detail::waitImpl(*this, dur);
return *this;
}
template <class T>
Future<T>&& Future<T>::wait(HighResDuration dur) && {
futures::detail::waitImpl(*this, dur);
return std::move(*this);
}
template <class T>
Future<T>& Future<T>::waitVia(DrivableExecutor* e) & {
futures::detail::waitViaImpl(*this, e);
return *this;
}
template <class T>
Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
futures::detail::waitViaImpl(*this, e);
return std::move(*this);
}
template <class T>
Future<T>& Future<T>::waitVia(TimedDrivableExecutor* e, HighResDuration dur) & {
futures::detail::waitViaImpl(*this, e, dur);
return *this;
}
template <class T>
Future<T>&& Future<T>::waitVia(
TimedDrivableExecutor* e,
HighResDuration dur) && {
futures::detail::waitViaImpl(*this, e, dur);
return std::move(*this);
}
template <class T>
T Future<T>::get() && {
wait();
return copy(std::move(*this)).value();
}
template <class T>
T Future<T>::get(HighResDuration dur) && {
wait(dur);
auto future = copy(std::move(*this));
if (!future.isReady()) {
throw_exception<FutureTimeout>();
}
return std::move(future).value();
}
template <class T>
Try<T>& Future<T>::getTry() {
return result();
}
template <class T>
T Future<T>::getVia(DrivableExecutor* e) {
return std::move(waitVia(e).value());
}
template <class T>
T Future<T>::getVia(TimedDrivableExecutor* e, HighResDuration dur) {
waitVia(e, dur);
if (!this->isReady()) {
throw_exception<FutureTimeout>();
}
return std::move(value());
}
template <class T>
Try<T>& Future<T>::getTryVia(DrivableExecutor* e) {
return waitVia(e).getTry();
}
template <class T>
Try<T>& Future<T>::getTryVia(TimedDrivableExecutor* e, HighResDuration dur) {
waitVia(e, dur);
if (!this->isReady()) {
throw_exception<FutureTimeout>();
}
return result();
}
namespace futures {
namespace detail {
template <class T>
struct TryEquals {
static bool equals(const Try<T>& t1, const Try<T>& t2) {
return t1.value() == t2.value();
}
};
} // namespace detail
} // namespace futures
template <class T>
Future<bool> Future<T>::willEqual(Future<T>& f) {
return collectAllSemiFuture(*this, f).toUnsafeFuture().thenValue(
[](const std::tuple<Try<T>, Try<T>>& t) {
if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) {
return futures::detail::TryEquals<T>::equals(
std::get<0>(t), std::get<1>(t));
} else {
return false;
}
});
}
template <class T>
template <class F>
Future<T> Future<T>::filter(F&& predicate) && {
return std::move(*this).thenValue([p = std::forward<F>(predicate)](T val) {
T const& valConstRef = val;
if (!p(valConstRef)) {
throw_exception<FuturePredicateDoesNotObtain>();
}
return val;
});
}
template <class F>
auto when(bool p, F&& thunk)
-> decltype(std::declval<invoke_result_t<F>>().unit()) {
return p ? std::forward<F>(thunk)().unit() : makeFuture();
}
template <class P, class F>
typename std::
enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type
whileDo(P&& predicate, F&& thunk) {
if (predicate()) {
auto future = thunk();
return std::move(future).deferExValue(
[predicate = std::forward<P>(predicate),
thunk = std::forward<F>(thunk)](auto&& ex, auto&&) mutable {
return whileDo(std::forward<P>(predicate), std::forward<F>(thunk))
.via(std::move(ex));
});
}
return makeSemiFuture();
}
template <class P, class F>
typename std::enable_if<isFuture<invoke_result_t<F>>::value, Future<Unit>>::type
whileDo(P&& predicate, F&& thunk) {
if (predicate()) {
auto future = thunk();
return std::move(future).thenValue(
[predicate = std::forward<P>(predicate),
thunk = std::forward<F>(thunk)](auto&&) mutable {
return whileDo(std::forward<P>(predicate), std::forward<F>(thunk));
});
}
return makeFuture();
}
template <class F>
auto times(const int n, F&& thunk) {
return folly::whileDo(
[n, count = std::make_unique<std::atomic<int>>(0)]() mutable {
return count->fetch_add(1, std::memory_order_relaxed) < n;
},
std::forward<F>(thunk));
}
namespace futures {
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>> mapValue(It first, It last, F func) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
results.push_back(std::move(*it).thenValue(func));
}
return results;
}
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>> mapTry(It first, It last, F func, int) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
results.push_back(std::move(*it).thenTry(func));
}
return results;
}
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>>
mapValue(Executor& exec, It first, It last, F func) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
results.push_back(std::move(*it).via(&exec).thenValue(func));
}
return results;
}
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>>
mapTry(Executor& exec, It first, It last, F func, int) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
results.push_back(std::move(*it).via(&exec).thenTry(func));
}
return results;
}
template <typename F, class Ensure>
auto ensure(F&& f, Ensure&& ensure) {
return makeSemiFuture()
.deferValue([f = std::forward<F>(f)](auto) mutable { return f(); })
.defer([ensure = std::forward<Ensure>(ensure)](auto resultTry) mutable {
ensure();
return std::move(resultTry).value();
});
}
template <class T>
void detachOn(folly::Executor::KeepAlive<> exec, folly::SemiFuture<T>&& fut) {
std::move(fut).via(exec).detach();
}
template <class T>
void detachOnGlobalCPUExecutor(folly::SemiFuture<T>&& fut) {
detachOn(folly::getGlobalCPUExecutor(), std::move(fut));
}
} // namespace futures
template <class Clock>
SemiFuture<Unit> Timekeeper::at(std::chrono::time_point<Clock> when) {
auto now = Clock::now();
if (when <= now) {
return makeSemiFuture();
}
return after(std::chrono::duration_cast<HighResDuration>(when - now));
}
} // namespace folly