mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-05 03:35:51 +00:00
Compare commits
892 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d22b25c030 | ||
|
|
d475994e02 | ||
|
|
f0bb3dfdfb | ||
|
|
e7f0b8eca6 | ||
|
|
0bab6a9fec | ||
|
|
9486fc416c | ||
|
|
fb63aa737a | ||
|
|
58a6ca1d3d | ||
|
|
505f029edb | ||
|
|
815659b898 | ||
|
|
07d16f280c | ||
|
|
f88ddc947c | ||
|
|
65ffdff40c | ||
|
|
c95dccfec6 | ||
|
|
fe83f471f5 | ||
|
|
d2953f602e | ||
|
|
9d07ddeae1 | ||
|
|
ef7810bc95 | ||
|
|
486539b3d3 | ||
|
|
990fb20a2a | ||
|
|
9b61a83721 | ||
|
|
f753519976 | ||
|
|
663e38dcdd | ||
|
|
7570b6489d | ||
|
|
c341d1a71e | ||
|
|
fa10e90c9d | ||
|
|
68501763dd | ||
|
|
cac1d555be | ||
|
|
25ff77c2fd | ||
|
|
2870c7f457 | ||
|
|
ab8d7a86ae | ||
|
|
809359e81e | ||
|
|
6e4cd5bc9c | ||
|
|
de018bd582 | ||
|
|
544642a6ea | ||
|
|
06737bb36f | ||
|
|
7efbfa2d20 | ||
|
|
4d5df92cbc | ||
|
|
1fcb2872b9 | ||
|
|
00c87ca2dd | ||
|
|
d474d68566 | ||
|
|
93e03804d0 | ||
|
|
4591658160 | ||
|
|
a2109b4bda | ||
|
|
deafea9c88 | ||
|
|
93f1a05f5c | ||
|
|
95a573b755 | ||
|
|
2a4623814c | ||
|
|
1017adf743 | ||
|
|
34fb12344c | ||
|
|
ce3358bdf8 | ||
|
|
1159dadfdb | ||
|
|
62516ef07f | ||
|
|
eecd305efd | ||
|
|
a83fa6b2b2 | ||
|
|
b2feafa94c | ||
|
|
2d234e500d | ||
|
|
fee0e7b20e | ||
|
|
a0f6429652 | ||
|
|
8f8b2ae4a3 | ||
|
|
9abdd16721 | ||
|
|
1e5963aeeb | ||
|
|
e25a83bb39 | ||
|
|
e3a67b13ff | ||
|
|
750cbb8399 | ||
|
|
045beb5f36 | ||
|
|
cd8234acba | ||
|
|
de85a7c2bd | ||
|
|
087301933a | ||
|
|
52333b8bd4 | ||
|
|
3768b3c3ca | ||
|
|
09b39e107d | ||
|
|
4b1155bf32 | ||
|
|
3c7fc31c95 | ||
|
|
da3881a486 | ||
|
|
ff12d9adaa | ||
|
|
528cf56f80 | ||
|
|
0ebe3f1f35 | ||
|
|
328680b6cd | ||
|
|
f9dca105a6 | ||
|
|
3664db61e7 | ||
|
|
49677aa799 | ||
|
|
27b771e2ba | ||
|
|
6527cdfa97 | ||
|
|
b2dbe8ef83 | ||
|
|
9bdb0774ad | ||
|
|
8c2ec2cfbe | ||
|
|
00f959ab7e | ||
|
|
f9c4070ad3 | ||
|
|
074325a7ea | ||
|
|
2f521a6a91 | ||
|
|
07959b3cc9 | ||
|
|
21faf8eaeb | ||
|
|
3e2b5dcc3d | ||
|
|
88a8433d31 | ||
|
|
81d418007a | ||
|
|
f88fcf55a3 | ||
|
|
561c8dea08 | ||
|
|
de92ac9e0b | ||
|
|
deead04a6a | ||
|
|
d4771a9b36 | ||
|
|
27b8415d0c | ||
|
|
e1e81e5d97 | ||
|
|
3705680d68 | ||
|
|
a01f546ae3 | ||
|
|
eabc905bac | ||
|
|
37588b6808 | ||
|
|
071db75f04 | ||
|
|
1e00940a90 | ||
|
|
b984566480 | ||
|
|
51aef120a1 | ||
|
|
636d722e8d | ||
|
|
e6da61120a | ||
|
|
b901e0dcf3 | ||
|
|
177b3c93c4 | ||
|
|
9996e4a57e | ||
|
|
4751b6a65c | ||
|
|
b3c79f5c2f | ||
|
|
0e53105ab5 | ||
|
|
0f2a657196 | ||
|
|
89aa2c7a6a | ||
|
|
4c843b6c66 | ||
|
|
130c7c5c58 | ||
|
|
76c364ec2d | ||
|
|
a549c94a15 | ||
|
|
6de8a6907f | ||
|
|
fc31562052 | ||
|
|
cdaa65c07a | ||
|
|
8a278cf9d6 | ||
|
|
97cecc18ce | ||
|
|
536db23e14 | ||
|
|
39d801fb9f | ||
|
|
6d707b21b2 | ||
|
|
2316fe5bbe | ||
|
|
16ec9d2bdb | ||
|
|
2ce4ce4309 | ||
|
|
858c3a5362 | ||
|
|
c124ad0dcd | ||
|
|
ed64c8bb29 | ||
|
|
2678360715 | ||
|
|
54e504dd5a | ||
|
|
b632a6b2cf | ||
|
|
b9e2ac38fa | ||
|
|
4d1c2a5798 | ||
|
|
c69d8a13b3 | ||
|
|
7c358cda17 | ||
|
|
1954a0eb2e | ||
|
|
5500701661 | ||
|
|
21918922f4 | ||
|
|
fad52c5917 | ||
|
|
306811d2a7 | ||
|
|
8b72f2ad79 | ||
|
|
1a1cb696f7 | ||
|
|
582c17b06b | ||
|
|
b156a49cff | ||
|
|
3943cfea06 | ||
|
|
97346c6618 | ||
|
|
474b824902 | ||
|
|
02b5572ccc | ||
|
|
4577ad60c7 | ||
|
|
e683c380e5 | ||
|
|
b660d82516 | ||
|
|
c0dda06499 | ||
|
|
65abd6307d | ||
|
|
6e713dc3b8 | ||
|
|
596a35acca | ||
|
|
4ad84a339f | ||
|
|
833435f8c2 | ||
|
|
4e4942e357 | ||
|
|
1fc8f0a33b | ||
|
|
1f433dea97 | ||
|
|
d8707cad2c | ||
|
|
a399b571ac | ||
|
|
d0e71225c4 | ||
|
|
19d4bf0ea5 | ||
|
|
9e519af887 | ||
|
|
29aa462bfd | ||
|
|
7a91872ee5 | ||
|
|
68307d1012 | ||
|
|
48cb707bb6 | ||
|
|
9322233b37 | ||
|
|
1daf1b9932 | ||
|
|
a3024352ba | ||
|
|
58f07a573f | ||
|
|
a05f33f6a7 | ||
|
|
968624971f | ||
|
|
57e77a5bd2 | ||
|
|
74c65cfdc5 | ||
|
|
399760fda9 | ||
|
|
67b8f95b1e | ||
|
|
d4d6acdf68 | ||
|
|
472baa8bac | ||
|
|
dd74c19858 | ||
|
|
b5f8d447a0 | ||
|
|
5f4a1917a6 | ||
|
|
cf71680aee | ||
|
|
f04b9131cc | ||
|
|
46861fac48 | ||
|
|
4620b667e7 | ||
|
|
49f43ccc0a | ||
|
|
63aa7284c4 | ||
|
|
286ade2d17 | ||
|
|
066d92ecfa | ||
|
|
c5ccabec38 | ||
|
|
548fedb859 | ||
|
|
8d5378a2ca | ||
|
|
bf1843be9e | ||
|
|
d50439cc4d | ||
|
|
6a8f313394 | ||
|
|
c211094d3e | ||
|
|
072b4f3b73 | ||
|
|
08cbcba4ee | ||
|
|
2a9171c623 | ||
|
|
8573679fbb | ||
|
|
811f244fc2 | ||
|
|
a31b2556a3 | ||
|
|
a0ad5cdbfe | ||
|
|
59cf668348 | ||
|
|
09acc26c50 | ||
|
|
0ae7bcff52 | ||
|
|
2210dbac94 | ||
|
|
6b2f654a30 | ||
|
|
3296ac5628 | ||
|
|
6d06cb29df | ||
|
|
b08c7d15cd | ||
|
|
940d620a96 | ||
|
|
a39fa8ae5f | ||
|
|
f859bf160a | ||
|
|
e0512930ae | ||
|
|
7bbf6c553f | ||
|
|
aeb335ebdc | ||
|
|
73ab408b3c | ||
|
|
f333a33f3d | ||
|
|
42b841735e | ||
|
|
e710bd2183 | ||
|
|
84556ba76a | ||
|
|
4eebea91d3 | ||
|
|
4ddadb8792 | ||
|
|
8e65d6288d | ||
|
|
8e18deb74f | ||
|
|
7ae1ad524b | ||
|
|
1ba1b3983a | ||
|
|
aabd6980ac | ||
|
|
3b19310252 | ||
|
|
8201805b28 | ||
|
|
7277c8478b | ||
|
|
963a0dd934 | ||
|
|
3108d58791 | ||
|
|
31b1a6a7e6 | ||
|
|
9ff65d0da4 | ||
|
|
c11abb42d1 | ||
|
|
7b6d81d812 | ||
|
|
ca0daad11f | ||
|
|
a0c4e685c5 | ||
|
|
b30f7a622c | ||
|
|
23f44f12bd | ||
|
|
6c17002e8a | ||
|
|
5dda088335 | ||
|
|
2427cce2c8 | ||
|
|
1c41dae51c | ||
|
|
1a6d72b14c | ||
|
|
3dc646e03e | ||
|
|
96328a8632 | ||
|
|
2cc4488d8e | ||
|
|
a6c2fe4761 | ||
|
|
ed905d3c3d | ||
|
|
81eadbd05c | ||
|
|
ef1e2f8595 | ||
|
|
fae7082049 | ||
|
|
a63de23156 | ||
|
|
8e0dda8480 | ||
|
|
ad7b9ff8b5 | ||
|
|
04f2d0787a | ||
|
|
2ad98a025e | ||
|
|
67516766a6 | ||
|
|
3b2ead3476 | ||
|
|
fc5be2b911 | ||
|
|
466e623dd6 | ||
|
|
a1b487c512 | ||
|
|
0902af8eb5 | ||
|
|
a8a4caf0e4 | ||
|
|
33478517a6 | ||
|
|
0d5a8ca300 | ||
|
|
da22f06d85 | ||
|
|
a918924923 | ||
|
|
5a9416fbcf | ||
|
|
582b5bb3ac | ||
|
|
abe4f1ba03 | ||
|
|
9dfbffa4b8 | ||
|
|
a25ba91876 | ||
|
|
334f109415 | ||
|
|
eb9eb3aa53 | ||
|
|
978c196c78 | ||
|
|
20e7cac743 | ||
|
|
b5d51214ff | ||
|
|
485d4b4897 | ||
|
|
fb6ecebbd1 | ||
|
|
0b69378a03 | ||
|
|
256c12f150 | ||
|
|
8d0349eee0 | ||
|
|
ab0548c9af | ||
|
|
4868bc4df7 | ||
|
|
0900dfe46f | ||
|
|
8dfe53ff7a | ||
|
|
1ae3328642 | ||
|
|
b9e0208aee | ||
|
|
497cc74adc | ||
|
|
eac3814fb5 | ||
|
|
bb331abeba | ||
|
|
6e20bd2dcd | ||
|
|
09961845b4 | ||
|
|
7eacd3bf57 | ||
|
|
72681fa7fb | ||
|
|
500bddebff | ||
|
|
4d3d46f41d | ||
|
|
82d8d9a092 | ||
|
|
30ff139a29 | ||
|
|
dc8420d32d | ||
|
|
bb29c8ba85 | ||
|
|
625780621b | ||
|
|
ea2589dd9c | ||
|
|
942336c454 | ||
|
|
90282707ab | ||
|
|
70f6c41ff7 | ||
|
|
184cf74daa | ||
|
|
a23fb06409 | ||
|
|
3638485977 | ||
|
|
75f3c52d53 | ||
|
|
364973a523 | ||
|
|
678c241962 | ||
|
|
a2aa938e10 | ||
|
|
48eb92e366 | ||
|
|
2894059b63 | ||
|
|
66a272debd | ||
|
|
7d089561c3 | ||
|
|
53f8e73b65 | ||
|
|
37bcf7899e | ||
|
|
86f662aa4a | ||
|
|
a1b958eaac | ||
|
|
06189b2584 | ||
|
|
0bf006cdae | ||
|
|
3f51eb7b63 | ||
|
|
b76443dbde | ||
|
|
5fc823ae08 | ||
|
|
68aec74b47 | ||
|
|
acd23682d1 | ||
|
|
b042397b9f | ||
|
|
951a8208b8 | ||
|
|
9b3c74a095 | ||
|
|
647c0e302a | ||
|
|
b3b22d7595 | ||
|
|
1ba0139683 | ||
|
|
15ef435054 | ||
|
|
1a95dfb300 | ||
|
|
6962bdb325 | ||
|
|
bbbb8b1411 | ||
|
|
0a5505f785 | ||
|
|
b6d11c08d0 | ||
|
|
0f26fbd701 | ||
|
|
a236c81b5f | ||
|
|
73700a483d | ||
|
|
6062f6b036 | ||
|
|
e29b0b150d | ||
|
|
4fe63f9f0d | ||
|
|
41879d8511 | ||
|
|
46d3ace6b7 | ||
|
|
2ac2fdfabd | ||
|
|
2aa5c74c2a | ||
|
|
d624ed1151 | ||
|
|
9bc132778f | ||
|
|
ac13c97f7e | ||
|
|
26dfa9231a | ||
|
|
072f2c93ef | ||
|
|
14fb246b67 | ||
|
|
99e4335f00 | ||
|
|
ea1a5a77c5 | ||
|
|
a815f455af | ||
|
|
b2c46db006 | ||
|
|
097b6f8e33 | ||
|
|
9aca3cb21c | ||
|
|
d1f589835c | ||
|
|
27e8a71f08 | ||
|
|
db531d905c | ||
|
|
d27ad6251c | ||
|
|
217a017195 | ||
|
|
9350f3085d | ||
|
|
018a030d97 | ||
|
|
052fc7be22 | ||
|
|
9e0d7241eb | ||
|
|
191e76a558 | ||
|
|
511978b1eb | ||
|
|
6beb7996ff | ||
|
|
c82b1b1853 | ||
|
|
a6ec4f91b0 | ||
|
|
75c28f3951 | ||
|
|
00a714d14d | ||
|
|
58a8a97177 | ||
|
|
97f1b41b4d | ||
|
|
8e58551475 | ||
|
|
e4fe965de0 | ||
|
|
3fefd43898 | ||
|
|
d9f20436e8 | ||
|
|
9df86ec8fd | ||
|
|
6602aff53d | ||
|
|
3ec36929f1 | ||
|
|
1f7d3d47a5 | ||
|
|
ab156cbcd1 | ||
|
|
4c43faec85 | ||
|
|
2d2cd3aab1 | ||
|
|
c5f017739d | ||
|
|
7437308bab | ||
|
|
fb6a0f10c3 | ||
|
|
d939c039f1 | ||
|
|
c568988640 | ||
|
|
ca00a595c3 | ||
|
|
add0784ae2 | ||
|
|
b2d84139a5 | ||
|
|
13a09113ba | ||
|
|
9b28ad136b | ||
|
|
cd9a4e6b57 | ||
|
|
f9596e1514 | ||
|
|
ac19affc11 | ||
|
|
0048efea8d | ||
|
|
54dc7017e1 | ||
|
|
beebfec3db | ||
|
|
ae6e9e2790 | ||
|
|
d1ec2eae83 | ||
|
|
dd49eb4d65 | ||
|
|
92d8a80b4e | ||
|
|
48f815ff70 | ||
|
|
948badc98b | ||
|
|
92bd8c4782 | ||
|
|
5bebac7ecb | ||
|
|
efa05c5c58 | ||
|
|
2c7a846c3e | ||
|
|
d10dba6b2e | ||
|
|
d683b47dee | ||
|
|
985af0e222 | ||
|
|
013520c9d4 | ||
|
|
00f5c9b34b | ||
|
|
b4168392b7 | ||
|
|
7cbe8572a3 | ||
|
|
05a183e30a | ||
|
|
2b37a4c7b0 | ||
|
|
8cca807ea1 | ||
|
|
4c3f059819 | ||
|
|
18fc854bf3 | ||
|
|
eff0271e7a | ||
|
|
5888f55bdd | ||
|
|
e62adfe4d4 | ||
|
|
6d19221e94 | ||
|
|
fcd689afbf | ||
|
|
58a9a2cc07 | ||
|
|
c226f9ecab | ||
|
|
8f514c8e1b | ||
|
|
38ed0bee79 | ||
|
|
a655662098 | ||
|
|
69dc722a7a | ||
|
|
32a7f42557 | ||
|
|
4a871a5726 | ||
|
|
909f4ffec9 | ||
|
|
d163e80e77 | ||
|
|
9b22c810ef | ||
|
|
426636f48d | ||
|
|
3518a51d4a | ||
|
|
cca5c86673 | ||
|
|
be828e834d | ||
|
|
7e82612cd5 | ||
|
|
8f4e722f73 | ||
|
|
ea11987609 | ||
|
|
807b4fd659 | ||
|
|
ac90baa831 | ||
|
|
794cd03b8f | ||
|
|
2f77ca416a | ||
|
|
8b0df758f3 | ||
|
|
c76e2b54a9 | ||
|
|
01d7d7bed9 | ||
|
|
c3bb005324 | ||
|
|
5d63086b69 | ||
|
|
f61caaae09 | ||
|
|
d90c2c0e11 | ||
|
|
321898f71e | ||
|
|
1dd50f4422 | ||
|
|
20b2e318eb | ||
|
|
19eff08e16 | ||
|
|
2d65ab4021 | ||
|
|
43deaaa5cf | ||
|
|
7c31a423e9 | ||
|
|
dc61fadc56 | ||
|
|
0d744cc253 | ||
|
|
1f372a3b08 | ||
|
|
b0c37d62fe | ||
|
|
e04dbdde19 | ||
|
|
5bd6fb27e6 | ||
|
|
b123b1a849 | ||
|
|
d16aa7f928 | ||
|
|
7dd41ffb5b | ||
|
|
a2151bfa47 | ||
|
|
0dc3cf07d0 | ||
|
|
ac1ebf403f | ||
|
|
5443e0f332 | ||
|
|
5856730ec3 | ||
|
|
ec6c09d995 | ||
|
|
ec6ee6f3bd | ||
|
|
dcbfa1524e | ||
|
|
51198c266f | ||
|
|
ed1a4b1283 | ||
|
|
0cbfc6079b | ||
|
|
e59293ec92 | ||
|
|
be1cede458 | ||
|
|
18d5021640 | ||
|
|
85fc59b28b | ||
|
|
63de1618e9 | ||
|
|
e3e72b3fc3 | ||
|
|
5c84f75bd9 | ||
|
|
b493db1d65 | ||
|
|
c3b815004c | ||
|
|
e6e090c542 | ||
|
|
fad8b0406e | ||
|
|
6d1796725b | ||
|
|
a1596dd3d1 | ||
|
|
927332dc01 | ||
|
|
cc77e4e9d8 | ||
|
|
86c868874a | ||
|
|
89b1859929 | ||
|
|
97e961a048 | ||
|
|
cc1fd7bc6a | ||
|
|
05b0e896aa | ||
|
|
2464607a09 | ||
|
|
776d934d66 | ||
|
|
be6bd6c9fb | ||
|
|
53f7846d2b | ||
|
|
c733eb6e43 | ||
|
|
11601bcccb | ||
|
|
3ff8902481 | ||
|
|
d105f6a2bd | ||
|
|
ed3be7a187 | ||
|
|
c8187e92af | ||
|
|
ec9ee5cc4b | ||
|
|
ff674a32e2 | ||
|
|
f22e91cb9e | ||
|
|
3efab6732f | ||
|
|
7c39867d56 | ||
|
|
237093dcd3 | ||
|
|
985db3fa16 | ||
|
|
e2e8f85b3c | ||
|
|
29ec27c377 | ||
|
|
4109e51e30 | ||
|
|
604ca590b6 | ||
|
|
feacf3ef13 | ||
|
|
9b76638351 | ||
|
|
aa89f46ae1 | ||
|
|
0f60a503f6 | ||
|
|
57f8328b0d | ||
|
|
f36fcf635c | ||
|
|
607520f2e6 | ||
|
|
02cb0f0499 | ||
|
|
ee5c600e29 | ||
|
|
ddb8268d5f | ||
|
|
06e80ec522 | ||
|
|
94c77c38f4 | ||
|
|
1a78d63f05 | ||
|
|
a602eb3b5d | ||
|
|
fb9fc26e58 | ||
|
|
c58450a44b | ||
|
|
76f62bd7c5 | ||
|
|
007723236e | ||
|
|
74b2d1f3fa | ||
|
|
b45b029ba7 | ||
|
|
fd59422a22 | ||
|
|
1f892029f8 | ||
|
|
846b8e339c | ||
|
|
5be3a1a34d | ||
|
|
c631cc5f92 | ||
|
|
9b40bc6835 | ||
|
|
b79ede018d | ||
|
|
3b82c0b1a1 | ||
|
|
0e4c865853 | ||
|
|
d67e38fa85 | ||
|
|
817a0256d2 | ||
|
|
36e88a25f6 | ||
|
|
0d2344c9a6 | ||
|
|
b839ae0552 | ||
|
|
5038a65acd | ||
|
|
ec3c4ff77a | ||
|
|
6a84f73d2a | ||
|
|
681f98ad07 | ||
|
|
8c1c7b48bb | ||
|
|
7f61499d3a | ||
|
|
3550a21294 | ||
|
|
048dcf8a3c | ||
|
|
89bc2bbc6f | ||
|
|
db30bfe352 | ||
|
|
baa0b474ba | ||
|
|
a76672c5f0 | ||
|
|
0079c9070d | ||
|
|
e1e8744052 | ||
|
|
f0231af0c0 | ||
|
|
9e6e7b5dd9 | ||
|
|
a48bcfeddb | ||
|
|
fb804bd1bd | ||
|
|
43d386fa6e | ||
|
|
68422ed5b4 | ||
|
|
03b8ae742e | ||
|
|
99afec18c5 | ||
|
|
27f0cae812 | ||
|
|
45eccf2ccf | ||
|
|
6d828ae476 | ||
|
|
1b6761286f | ||
|
|
112234c8a9 | ||
|
|
ae1daafd44 | ||
|
|
255e5eedf6 | ||
|
|
0c6c9774a9 | ||
|
|
a04fa04d07 | ||
|
|
03174a0d6a | ||
|
|
10d88114e4 | ||
|
|
7238e23e8a | ||
|
|
128e6a4125 | ||
|
|
3e4833acca | ||
|
|
1148b422d5 | ||
|
|
c6b5ccdd22 | ||
|
|
627114b1ac | ||
|
|
e8893e9678 | ||
|
|
809a2ce7bf | ||
|
|
86ba8ffca6 | ||
|
|
fe18b54d20 | ||
|
|
9812d69aa2 | ||
|
|
b5e1a600a1 | ||
|
|
8bdfa860ee | ||
|
|
7f84f44a74 | ||
|
|
6234f0040d | ||
|
|
1b18e16d1f | ||
|
|
51332a91b9 | ||
|
|
c063b940bb | ||
|
|
3c23699a87 | ||
|
|
00caefc1c3 | ||
|
|
6be6e68165 | ||
|
|
8866c2dc1f | ||
|
|
adb6c39b92 | ||
|
|
955a791ab1 | ||
|
|
6c32b174e8 | ||
|
|
bbc9e886dd | ||
|
|
e1f80ef6a6 | ||
|
|
a4e81fb6ad | ||
|
|
d3159aba88 | ||
|
|
afd22025d7 | ||
|
|
fd6e0e788e | ||
|
|
cb8ba44f62 | ||
|
|
140be155f2 | ||
|
|
68547c328e | ||
|
|
3174372081 | ||
|
|
eaec90c897 | ||
|
|
1d1587c2ac | ||
|
|
4a30b259b2 | ||
|
|
e927160ff5 | ||
|
|
aac657a2e6 | ||
|
|
8c2f9e7388 | ||
|
|
c8cedfc06e | ||
|
|
6be9c41064 | ||
|
|
62ffbc97ba | ||
|
|
0bbad54a36 | ||
|
|
044e3b09db | ||
|
|
d6a12f791f | ||
|
|
bf2af27452 | ||
|
|
98f1169ac3 | ||
|
|
816b3a4efb | ||
|
|
941503c564 | ||
|
|
e513380e08 | ||
|
|
a050ba6fa4 | ||
|
|
3594c3b290 | ||
|
|
8d15b4059d | ||
|
|
403e2247a2 | ||
|
|
ddcbb9c38c | ||
|
|
4b7b5a41a2 | ||
|
|
b921791b34 | ||
|
|
a690c579c1 | ||
|
|
60d5ca4523 | ||
|
|
de7e3c578e | ||
|
|
f48c2c24b6 | ||
|
|
ef544b108f | ||
|
|
7abfd354f8 | ||
|
|
65009c77a7 | ||
|
|
2d9d00c2ef | ||
|
|
12495f0e65 | ||
|
|
e6e2873b4f | ||
|
|
de210fef95 | ||
|
|
89748aad37 | ||
|
|
aa64bdc41c | ||
|
|
abcb1dce57 | ||
|
|
5db25ede25 | ||
|
|
55015f2730 | ||
|
|
9f87e36545 | ||
|
|
f1522b8fef | ||
|
|
4819714edf | ||
|
|
eac898a26f | ||
|
|
e892e9bb4e | ||
|
|
e26a3a1d84 | ||
|
|
5a06121a85 | ||
|
|
a29ced1feb | ||
|
|
bcd6e9d676 | ||
|
|
6c267d6388 | ||
|
|
02107c3023 | ||
|
|
1277f70c47 | ||
|
|
8e75064fe3 | ||
|
|
cca092f5d1 | ||
|
|
d71211185e | ||
|
|
c95211e572 | ||
|
|
57d55cebe4 | ||
|
|
0950ef3984 | ||
|
|
91e0cc84ef | ||
|
|
2e1167fbd9 | ||
|
|
5aa784922f | ||
|
|
c06aa4ff55 | ||
|
|
b1d029e5b7 | ||
|
|
649b20a5f2 | ||
|
|
81a4711e66 | ||
|
|
37216bed4d | ||
|
|
4ef0f5d6a9 | ||
|
|
369bd0f36c | ||
|
|
0f30b55cba | ||
|
|
e09d6a0a9b | ||
|
|
3077892c3b | ||
|
|
027fba44ed | ||
|
|
5f1a8670dc | ||
|
|
65df4b9daf | ||
|
|
5b8d104fb1 | ||
|
|
75b53ab171 | ||
|
|
0d5bd937b1 | ||
|
|
98b630d8db | ||
|
|
3526cabd7b | ||
|
|
a709167801 | ||
|
|
dda831e09e | ||
|
|
2070b2b1bd | ||
|
|
7de2d49345 | ||
|
|
0bcaf70e4a | ||
|
|
af0471fb7d | ||
|
|
e7d043e4df | ||
|
|
a9f6d67bba | ||
|
|
e256716da8 | ||
|
|
825ac4aca1 | ||
|
|
b1973db1f5 | ||
|
|
35598d7933 | ||
|
|
01fda4c30e | ||
|
|
1391a11e46 | ||
|
|
bbfbdabe76 | ||
|
|
e22c1c3495 | ||
|
|
1ee8b3903c | ||
|
|
f5db655c47 | ||
|
|
11a09879a9 | ||
|
|
f30fe1b699 | ||
|
|
d022fa68cf | ||
|
|
99743e0fe0 | ||
|
|
97075405cc | ||
|
|
b36e534197 | ||
|
|
f52c1657bf | ||
|
|
a85afbe409 | ||
|
|
88060183f6 | ||
|
|
f37cf6277d | ||
|
|
b81e22939b | ||
|
|
47532b12dd | ||
|
|
11117c75b7 | ||
|
|
da5929cd18 | ||
|
|
835236a35c | ||
|
|
eb33ae0d71 | ||
|
|
be44586f85 | ||
|
|
34ee13a404 | ||
|
|
b102afcd3b | ||
|
|
e005dd5921 | ||
|
|
640e9482c4 | ||
|
|
d7ca14b18d | ||
|
|
2780a5768a | ||
|
|
2cc2d982a8 | ||
|
|
e869713990 | ||
|
|
85e8a71918 | ||
|
|
551643ff64 | ||
|
|
d7086bfc5e | ||
|
|
174303195b | ||
|
|
f2d2bdafd3 | ||
|
|
b37c537b4a | ||
|
|
4170bcd294 | ||
|
|
3602e19dcd | ||
|
|
ef7cb16dcd | ||
|
|
dea3c27e95 | ||
|
|
68c089924a | ||
|
|
ab08478e86 | ||
|
|
99dbc447f4 | ||
|
|
7b9a5e8753 | ||
|
|
db4a1dfaa4 | ||
|
|
bab6ab53d5 | ||
|
|
838b5befc6 | ||
|
|
bea6302759 | ||
|
|
1959a93f28 | ||
|
|
364206b4e4 | ||
|
|
451d8ed488 | ||
|
|
444aba567f | ||
|
|
8b59a7b07b | ||
|
|
21485ec003 | ||
|
|
2bea9a8739 | ||
|
|
9ba8ea679c | ||
|
|
8908cdbfd5 | ||
|
|
942e71b0ce | ||
|
|
f14333012b | ||
|
|
c21a53a3ea | ||
|
|
4ececd0204 | ||
|
|
b0533a91fe | ||
|
|
5e827ba863 | ||
|
|
0506a15209 | ||
|
|
5dc9169f98 | ||
|
|
384370b433 | ||
|
|
a75631da56 | ||
|
|
37bfafe3bb | ||
|
|
76589f5084 | ||
|
|
9aaabaebcd | ||
|
|
64e729fba6 | ||
|
|
ce99820467 | ||
|
|
663d5c9cee | ||
|
|
8b86b81fc9 | ||
|
|
382b358bdf | ||
|
|
35836c9896 | ||
|
|
8e95ee6ed3 | ||
|
|
647acebdbf | ||
|
|
a39e973abe | ||
|
|
3a2e770e52 | ||
|
|
c17cfe9ea6 | ||
|
|
c5a40141fe | ||
|
|
32aabc8999 | ||
|
|
e8eadae116 | ||
|
|
6704a5190e | ||
|
|
a2f90da10c | ||
|
|
beb50496a4 | ||
|
|
9d897001b6 | ||
|
|
651c9c8be7 | ||
|
|
578fee5f74 | ||
|
|
568226e1ce | ||
|
|
54fe17d5eb | ||
|
|
d744e23e5e | ||
|
|
e7fc07869d | ||
|
|
02412b2ba4 | ||
|
|
45b7a6cdef | ||
|
|
96b918fb10 | ||
|
|
9d3e3058bc | ||
|
|
c221cfde7a | ||
|
|
54a6d21903 | ||
|
|
4e19c7cda1 | ||
|
|
55af90fb68 | ||
|
|
dfae891018 | ||
|
|
6e0d6bdba4 | ||
|
|
48c4eb3ad3 | ||
|
|
86147a6c6b | ||
|
|
dd3ac6a154 | ||
|
|
0df51dc9e4 | ||
|
|
a110a01e0d | ||
|
|
7776781560 | ||
|
|
958bc5866d | ||
|
|
1eccbd5e9d | ||
|
|
48bacf189d | ||
|
|
d571788172 | ||
|
|
2d04fc4641 | ||
|
|
04b5c7f447 | ||
|
|
d0e4352582 | ||
|
|
f342a70fd5 | ||
|
|
6a8f054b2f | ||
|
|
d9b1b56c98 | ||
|
|
6616cd86ef | ||
|
|
2333a38cd7 | ||
|
|
93f983ad71 | ||
|
|
0a58333edc | ||
|
|
191562c13c | ||
|
|
7fe3d3dc1b | ||
|
|
15dfaeb666 | ||
|
|
4eb3504a75 | ||
|
|
2ce0ada8f5 | ||
|
|
59fe53781f | ||
|
|
bb941354ce | ||
|
|
0feccfd7b0 | ||
|
|
461a710738 | ||
|
|
4c605a79b7 | ||
|
|
4952ea2707 | ||
|
|
b064f66ea2 | ||
|
|
7cf0ce22b1 | ||
|
|
d22b236d2d | ||
|
|
462a1394d7 | ||
|
|
9bfd8c923d | ||
|
|
8c65adbffe | ||
|
|
fe1b8d253c | ||
|
|
39caac637c | ||
|
|
c98b24dbe6 | ||
|
|
6eec84144a | ||
|
|
eb121e9312 | ||
|
|
44aa45f307 | ||
|
|
ff127d2b25 | ||
|
|
9bc96bc558 |
@@ -1,10 +0,0 @@
|
||||
;; Emacs - Code style and formatting settings
|
||||
|
||||
;; C++
|
||||
((c++-mode
|
||||
(indent-tabs-mode . t)
|
||||
(tab-width . 4)
|
||||
(c-basic-offset . 4)))
|
||||
|
||||
;; Headers should open in C++ mode
|
||||
((c-mode . ((mode . c++))))
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -24,16 +24,18 @@ Debug/*.*
|
||||
Release/*.*
|
||||
|
||||
# Ignore locally installed node_modules
|
||||
node_modules
|
||||
/node_modules
|
||||
|
||||
# Ignore tmp directory.
|
||||
tmp
|
||||
|
||||
# Ignore database directory.
|
||||
db
|
||||
db/*.db
|
||||
db/*.db-*
|
||||
|
||||
# Ignore debug logs
|
||||
debug_log.txt
|
||||
|
||||
# Ignore customized configs
|
||||
rippled.cfg
|
||||
validators.txt
|
||||
@@ -67,3 +69,6 @@ My Amplifier XE Results - RippleD
|
||||
# KeyvaDB files
|
||||
*.key
|
||||
*.val
|
||||
|
||||
# Compiler intermediate output
|
||||
/out.txt
|
||||
|
||||
23
.travis.yml
Normal file
23
.travis.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq python-software-properties
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo add-apt-repository -y ppa:boost-latest/ppa
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libboost1.54-all-dev protobuf-compiler libprotobuf-dev libssl-dev exuberant-ctags
|
||||
- sudo apt-get install -qq gcc-4.8
|
||||
- sudo apt-get install -qq g++-4.8
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 40 --slave /usr/bin/g++ g++ /usr/bin/g++-4.8
|
||||
- sudo update-alternatives --set gcc /usr/bin/gcc-4.8
|
||||
- g++ -v
|
||||
|
||||
script: scons && ./build/rippled --unittest && npm install && npm test
|
||||
notifications:
|
||||
email:
|
||||
false
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#ripple-dev"
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
# Ripple protocol buffers
|
||||
|
||||
PROTOS = ../../modules/ripple_data/protocol/ripple.proto
|
||||
PROTOS = ../../src/ripple_data/protocol/ripple.proto
|
||||
PROTOS_DIR = ../../build/proto
|
||||
|
||||
# Google Protocol Buffers support
|
||||
@@ -31,7 +31,9 @@ TEMPLATE = app
|
||||
CONFIG += console thread warn_off
|
||||
CONFIG -= qt gui
|
||||
|
||||
linux-gg++:QMAKE_CXXFLAGS += \
|
||||
DEFINES += _DEBUG
|
||||
|
||||
linux-g++:QMAKE_CXXFLAGS += \
|
||||
-Wall \
|
||||
-Wno-sign-compare \
|
||||
-Wno-char-subscripts \
|
||||
@@ -39,52 +41,62 @@ linux-gg++:QMAKE_CXXFLAGS += \
|
||||
-Wno-unused-parameter \
|
||||
-Wformat \
|
||||
-O0 \
|
||||
-std=c++11 \
|
||||
-pthread
|
||||
|
||||
INCLUDEPATH += \
|
||||
"../.." \
|
||||
"../../Subtrees" \
|
||||
"../../Subtrees/leveldb/" \
|
||||
"../../Subtrees/leveldb/port" \
|
||||
"../../Subtrees/leveldb/include" \
|
||||
"../../src" \
|
||||
"../../src/leveldb/" \
|
||||
"../../src/leveldb/port" \
|
||||
"../../src/leveldb/include" \
|
||||
$${PROTOS_DIR}
|
||||
|
||||
OTHER_FILES += \
|
||||
$$files(../../Subtrees/beast/*) \
|
||||
$$files(../../Subtrees/beast/modules/beast_basics/diagnostic/*)
|
||||
# $$files(../../src/*, true) \
|
||||
# $$files(../../src/beast/*) \
|
||||
# $$files(../../src/beast/modules/beast_basics/diagnostic/*)
|
||||
# $$files(../../src/beast/modules/beast_core/, true)
|
||||
|
||||
# $$files(../../Subtrees/beast/modules/beast_core/, true)
|
||||
# $$files(../../modules/*, true) \
|
||||
|
||||
UI_HEADERS_DIR += ../../modules/ripple_basics
|
||||
UI_HEADERS_DIR += ../../src/ripple_basics
|
||||
|
||||
# ---------
|
||||
# New style
|
||||
#
|
||||
SOURCES += \
|
||||
../../Subtrees/beast/modules/beast_asio/beast_asio.cpp \
|
||||
../../Subtrees/beast/modules/beast_basics/beast_basics.cpp \
|
||||
../../Subtrees/beast/modules/beast_core/beast_core.cpp \
|
||||
../../Subtrees/beast/modules/beast_crypto/beast_crypto.cpp \
|
||||
../../Subtrees/beast/modules/beast_db/beast_db.cpp \
|
||||
../../Subtrees/beast/modules/beast_sqdb/beast_sqdb.cpp \
|
||||
../../Subtrees/beast/modules/beast_sqlite/beast_sqlite.c \
|
||||
../../modules/ripple_app/ripple_app_pt1.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt2.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt3.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt4.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt5.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt6.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt7.cpp \
|
||||
../../modules/ripple_app/ripple_app_pt8.cpp \
|
||||
../../modules/ripple_asio/ripple_asio.cpp \
|
||||
../../modules/ripple_basics/ripple_basics.cpp \
|
||||
../../modules/ripple_core/ripple_core.cpp \
|
||||
../../modules/ripple_client/ripple_client.cpp \
|
||||
../../modules/ripple_data/ripple_data.cpp \
|
||||
../../modules/ripple_hyperleveldb/ripple_hyperleveldb.cpp \
|
||||
../../modules/ripple_json/ripple_json.cpp \
|
||||
../../modules/ripple_leveldb/ripple_leveldb.cpp \
|
||||
../../modules/ripple_mdb/ripple_mdb.c \
|
||||
../../modules/ripple_net/ripple_net.cpp \
|
||||
../../modules/ripple_websocket/ripple_websocket.cpp
|
||||
../../src/ripple/beast/ripple_beast.cpp \
|
||||
../../src/ripple/beast/ripple_beastc.c \
|
||||
../../src/ripple/common/ripple_common.cpp \
|
||||
../../src/ripple/http/ripple_http.cpp \
|
||||
../../src/ripple/json/ripple_json.cpp \
|
||||
../../src/ripple/peerfinder/ripple_peerfinder.cpp \
|
||||
../../src/ripple/resource/ripple_resource.cpp \
|
||||
../../src/ripple/rpc/ripple_rpc.cpp \
|
||||
../../src/ripple/sitefiles/ripple_sitefiles.cpp \
|
||||
../../src/ripple/sslutil/ripple_sslutil.cpp \
|
||||
../../src/ripple/testoverlay/ripple_testoverlay.cpp \
|
||||
../../src/ripple/types/ripple_types.cpp \
|
||||
../../src/ripple/validators/ripple_validators.cpp
|
||||
|
||||
# ---------
|
||||
# Old style
|
||||
#
|
||||
SOURCES += \
|
||||
../../src/ripple_app/ripple_app.cpp \
|
||||
../../src/ripple_app/ripple_app_pt1.cpp \
|
||||
../../src/ripple_app/ripple_app_pt2.cpp \
|
||||
../../src/ripple_app/ripple_app_pt3.cpp \
|
||||
../../src/ripple_app/ripple_app_pt4.cpp \
|
||||
../../src/ripple_app/ripple_app_pt5.cpp \
|
||||
../../src/ripple_app/ripple_app_pt6.cpp \
|
||||
../../src/ripple_app/ripple_app_pt7.cpp \
|
||||
../../src/ripple_app/ripple_app_pt8.cpp \
|
||||
../../src/ripple_basics/ripple_basics.cpp \
|
||||
../../src/ripple_core/ripple_core.cpp \
|
||||
../../src/ripple_data/ripple_data.cpp \
|
||||
../../src/ripple_hyperleveldb/ripple_hyperleveldb.cpp \
|
||||
../../src/ripple_leveldb/ripple_leveldb.cpp \
|
||||
../../src/ripple_net/ripple_net.cpp \
|
||||
../../src/ripple_websocket/ripple_websocket.cpp
|
||||
|
||||
LIBS += \
|
||||
-lboost_date_time-mt\
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Express 2012 for Windows Desktop
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "beast", "..\..\Subtrees\beast\Builds\VisualStudio2012\beast.vcxproj", "{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}"
|
||||
EndProject
|
||||
# Visual Studio 2012
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RippleD", "RippleD.vcxproj", "{B7F39ECD-473C-484D-BC34-31F8362506A5}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "beast", "..\..\src\beast\Builds\VisualStudio2012\beast.vcxproj", "{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
@@ -13,14 +13,6 @@ Global
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|x64.Build.0 = Debug|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|Win32.Build.0 = Release|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|x64.ActiveCfg = Release|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|x64.Build.0 = Release|x64
|
||||
{B7F39ECD-473C-484D-BC34-31F8362506A5}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B7F39ECD-473C-484D-BC34-31F8362506A5}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B7F39ECD-473C-484D-BC34-31F8362506A5}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -29,6 +21,14 @@ Global
|
||||
{B7F39ECD-473C-484D-BC34-31F8362506A5}.Release|Win32.Build.0 = Release|Win32
|
||||
{B7F39ECD-473C-484D-BC34-31F8362506A5}.Release|x64.ActiveCfg = Release|x64
|
||||
{B7F39ECD-473C-484D-BC34-31F8362506A5}.Release|x64.Build.0 = Release|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Debug|x64.Build.0 = Debug|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|Win32.Build.0 = Release|Win32
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|x64.ActiveCfg = Release|x64
|
||||
{73C5A0F0-7629-4DE7-9194-BE7AC6C19535}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
<PreprocessorDefinitions>_VARIADIC_MAX=10;_WIN32_WINNT=0x0600;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<AdditionalIncludeDirectories>$(RepoDir);$(RepoDir)\src\cpp\protobuf\src;$(RepoDir)\src\cpp\protobuf\vsprojects;$(RepoDir)\build\proto;$(RepoDir)\Subtrees;$(RepoDir)\Subtrees\leveldb;$(RepoDir)\Subtrees\leveldb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(RepoDir)\src\protobuf\src;$(RepoDir)\src\protobuf\vsprojects;$(RepoDir)\src;$(RepoDir)\src\leveldb;$(RepoDir)\src\leveldb\include;$(RepoDir)\build\proto;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<DisableSpecificWarnings>4018;4244</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
Vinnie Falco's Change Log
|
||||
|
||||
2013/08/07
|
||||
|
||||
- Add 'print' test that shows all the tests
|
||||
- Improved "package.test" match string format when choosing tests to run
|
||||
|
||||
2013/08/04
|
||||
|
||||
- Begin reworking of socket code
|
||||
|
||||
2013/08/01
|
||||
|
||||
- Add beast::SemanticVersion
|
||||
- Change rippled to use SemanticVersion
|
||||
- Add runStartup to UnitTest, allowing forced tests on startup
|
||||
- Force rippled BuildInfo unit test to run on every startup
|
||||
|
||||
2013/07/31
|
||||
|
||||
- Add "hostname" to server_info output
|
||||
- Replace "build_version" in server_info with the actual build version
|
||||
- Remove "client_version" from server_info
|
||||
|
||||
2013/07/30
|
||||
|
||||
- Add FatalErrorReporter to main
|
||||
- Rewrote the build and protocol version numbering code
|
||||
@@ -1,487 +0,0 @@
|
||||
--------------------------------------------------------------------------------
|
||||
RIPPLE TODO
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
REMINDER: KEEP CHANGE LOG UP TO DATE
|
||||
|
||||
Vinnie's List: Changes day to day, descending priority
|
||||
(Items marked '*' can be handled by others.)
|
||||
|
||||
- beast::Socket integration in Ripple
|
||||
- Socket implementation for PROXY protocol
|
||||
- Socket that supports multiple protcols
|
||||
- Unit tests for boost::asio wrappers
|
||||
- Review boost::asio wrappers and consolidation of network code in ripple_net
|
||||
- Deeply create directories specified in config settings
|
||||
- Finish unit tests and code for Validators
|
||||
* Document the command line options for the beast unit test framework
|
||||
|
||||
David Features:
|
||||
- override config items from command line
|
||||
- change config via RPC, this is for debugging
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
- Refactor Section code into ConfigFile
|
||||
|
||||
- Improved Mutex to track deadlocks
|
||||
|
||||
- Work on KeyvaDB
|
||||
|
||||
- Allow skipped/disabled unit tests and reporting.
|
||||
|
||||
- Supress useless gcc warnings
|
||||
http://stackoverflow.com/questions/3378560/how-to-disable-gcc-warnings-for-a-few-lines-of-code
|
||||
|
||||
- Get rid of boost::filesystem
|
||||
|
||||
- Make some fatal_assert macro that calls FatalError
|
||||
|
||||
- What the heck is up with site_scons/site_tools/protoc.py?
|
||||
|
||||
- Figure out the right behavior of ProtectedCall
|
||||
|
||||
- Do something about the throw() reporting weaknesses:
|
||||
* Make sure all Sconstruct and .pro builds have debug symbols in release
|
||||
* Replace all throw with beast::Throw()
|
||||
(Only in ripple sources, not in Subtrees/, protobuf, or websocket)
|
||||
- Improved Beast exception object, provides __FILE__ and __LINE__
|
||||
- Add file and line capabilities to beast::Throw()
|
||||
- Allow beast::Throw to be hooked for logging
|
||||
- Add stack trace capability to beast::Throw() diagnostics via the hook
|
||||
(use the existing beast::SystemStats::getStackBacktrace())
|
||||
- Implement getStackBacktrace for BEAST_BSD targets
|
||||
- Add UnhandledExceptionCatcher to beast
|
||||
- Return EXIT_FAILURE on unhandled exception
|
||||
|
||||
- Option to print the list of available unit tests
|
||||
|
||||
- Add convenience variadic functions to JobQueue that do the bind for you
|
||||
|
||||
- Consolidate databases
|
||||
|
||||
- Figure out why we need WAL sqlite mode if we no longer use sqlite for the node store
|
||||
|
||||
- Add "skipped" field to beginTestCase() to disable a test but still record
|
||||
that it was skipped in the output. Like for mdb import.
|
||||
|
||||
- use beast DeadlineTimer for sweep in Application
|
||||
|
||||
- Make SNTP Client have its own io_service
|
||||
|
||||
- Get rid of 'ref' typedefs that really mean const&
|
||||
|
||||
- Use secp256k1 from beast
|
||||
|
||||
- Fix xsd/dtd line in JUnit XML output
|
||||
|
||||
- Get rid of the WriteLog() stuff in the ripple tests and make it report the
|
||||
message directly to the UnitTest object. Then update the JUnit XML output
|
||||
routines to also write the auxiliary messages.
|
||||
|
||||
* Take away the "I" prefix from abstract interface classes, in both the class
|
||||
name and the file name. It is messing up sorting in the IDE. Use "Imp" or
|
||||
suffix for implementations.
|
||||
|
||||
* Restyle all the macros in ripple_ConfigSection.h
|
||||
|
||||
- Move src/protobuf to Subtrees and deal with protobuf_core.cpp
|
||||
|
||||
- Replace home-made database wrappers with beast::sqdb
|
||||
|
||||
- Use static creation member functions instead of endless constructor
|
||||
variations in base_uint, uint256, and family.
|
||||
|
||||
- Raise the warning level and fix everything
|
||||
|
||||
- Replace base_uint and uintXXX with UnsignedInteger
|
||||
* Need to specialize UnsignedInteger to work efficiently with 4 and 8 byte
|
||||
multiples of the size.
|
||||
|
||||
- Rewrite boost program_options in Beast
|
||||
|
||||
- Replace endian conversion calls with beast calls:
|
||||
htobe32, be32toh, ntohl, etc...
|
||||
Start by removing the system headers which provide these routines, if possible
|
||||
|
||||
- Rename RPCHandler to CallHandler
|
||||
|
||||
- See if UniqueNodeList is really used, and if its not used remove it. If
|
||||
only some small part of it is used, then delete the rest. David says
|
||||
that it is broken anyway.
|
||||
|
||||
- Tidy up convenience functions in RPC.h
|
||||
|
||||
- Maybe rename RPCServer to RPCClientServicer
|
||||
|
||||
- Profile/VTune the application to identify hot spots
|
||||
* Determine why rippled has a slow startup on Windows
|
||||
* Improve the performance when running all unit tests on Windows
|
||||
|
||||
- Rename "fullBelow" to something like haveAllDescendants or haveAllChildren.
|
||||
|
||||
- Class to represent IP and Port number, with members to print, check syntax,
|
||||
etc... replace the boost calls.
|
||||
|
||||
- Remove dependence on JobQueue, LoadFeeTrack, and NetworkOPs from LoadManager
|
||||
by providing an observer (beast::ListenerList or Listeners). This way
|
||||
LoadManager does not need stopThread() function.
|
||||
|
||||
- Rewrite Sustain to use Beast and work on Windows as well
|
||||
* Do not enable watchdog process if a debugger is attached
|
||||
|
||||
- Make separate LevelDB VS2012 project for source browsing, leave ony the unity
|
||||
.cpp in the main RippleD project
|
||||
|
||||
- Add LevelDB unity .cpp to the LevelDB fork
|
||||
|
||||
- Make sure the leak detector output appears on Linux and FreeBSD debug builds.
|
||||
|
||||
- Create SharedData <LoadState>, move all load related state variables currently
|
||||
protected by separated mutexes in different classes into the LoadState, and
|
||||
use read/write locking semantics to update the values. Later, use Listeners
|
||||
to notify dependent code to resolve the dependency inversion.
|
||||
|
||||
- Rename LoadMonitor to LoadMeter, change LoadEvent to LoadMeter::ScopedSample
|
||||
|
||||
- Rename LedgerMaster to Ledgers, create ILedgers interface.
|
||||
|
||||
- Restructure the ripple sources to have this directory structure:
|
||||
/Source/ripple/ripple_core/ripple_core.h
|
||||
/...
|
||||
/Source/Subtrees/... ?
|
||||
PROBLEM: Where to put BeastConfig.h ?
|
||||
|
||||
- Figure out where previous ledgers go after a call to LedgerMaster::pushLedger()
|
||||
and see if it is possible to clean up the leaks on exit.
|
||||
|
||||
- Replace all NULL with nullptr
|
||||
|
||||
- Add ICore interface (incremental replacement for Application)
|
||||
|
||||
- Make TxFormats a member of ICore instead of a singleton.
|
||||
PROBLEM: STObject derived classes like STInt16 make direct use of the
|
||||
singleton. It might have to remain a singleton. At the very least,
|
||||
it should be a SharedSingleton to resolve ordering issues.
|
||||
|
||||
- Rename include guards to boost style, e.g. RIPPLE_LOG_H_INCLUDED
|
||||
|
||||
- Replace C11X with BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
|
||||
- Fix all leaks on exit (!)
|
||||
Say there's a leak, a ledger that can never be accessed is locked in some
|
||||
structure. If the organized teardown code frees that structure, the leak
|
||||
will not be reported.
|
||||
Yes, so you'll detect some small subset of leaks that way.
|
||||
You'll still have to be vigilant for the leaks that won't detect.
|
||||
The problem is ordering. There are lots of circular dependencies.
|
||||
The biggest problem is the order of destruction of global objects. (I think)
|
||||
Getting rid of global objects is a good solution to that.
|
||||
Vinnie Falco: Those I can resolve with my ReferenceCountedSingleton. And
|
||||
yeah thats a good approach, one that I am doing slowly anyway
|
||||
Yeah, that's good for other reasons too, not just the unpredictability of
|
||||
creation order that can hide bugs.
|
||||
There may also just be some missing destructors.
|
||||
Some of it may be things being shut down in the wrong order. Like if you shut
|
||||
down the cache and then something that uses the cache, objects may get
|
||||
put in the cache after it was shut down.
|
||||
|
||||
- Remove "ENABLE_INSECURE" when the time is right.
|
||||
|
||||
- lift unique_ptr / auto_ptr into ripple namespace,
|
||||
or replace with ScopedPointer (preferred)
|
||||
|
||||
- Make LevelDB and Ripple code work with both Unicode and non-Unicode Windows APIs
|
||||
|
||||
- Go searching through VFALCO notes and fix everything
|
||||
|
||||
- Deal with function-level statics used for SqliteDatabase (like in
|
||||
HSBESQLite::visitAll)
|
||||
|
||||
- Document in order:
|
||||
SerializedType
|
||||
STObject
|
||||
SerializedLedgerEntry
|
||||
|
||||
- Replace uint160, uint256 in argument lists, template parameter lists, and
|
||||
data members with tyepdefs from ripple_ProtocolTypes.h
|
||||
|
||||
- Consolidate SQLite database classes: DatabaseCon, Database, SqliteDatabase.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
LEVELDB TODO
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
- Add VisualStudio 2012 project file to our fork
|
||||
|
||||
- Add LevelDB unity .cpp and .h to our fork
|
||||
|
||||
- Replace Beast specific platform macros with universal macros so that the
|
||||
unity doesn't require Beast
|
||||
|
||||
- Submit LevelDB fork changes to Bitcoin upstream
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
WEBSOCKET TODO
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
*** Figure out how hard we want to fork websocket first **
|
||||
|
||||
- Think about stripping the ripple specifics out of AutoSocket, make AutoSocket
|
||||
part of our websocketpp fork
|
||||
|
||||
- Regroup all the sources together in one directory
|
||||
|
||||
- Strip includes and enforce unity
|
||||
|
||||
- Put a new front-end on websocket to hide ALL of their classes and templates
|
||||
from the host application, make this part of the websocket fork
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
PROTOCOL BUFFERS TODO
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
- Create/maintain the protobuf Git repo (original uses SVN)
|
||||
|
||||
- Update the subtree
|
||||
|
||||
- Make a Visual Studio 2012 Project for source browsing
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
NOTES
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
LoadEvent
|
||||
|
||||
Is referenced with both a shared pointer and an auto pointer.
|
||||
Should be named LoadMeter::ScopedSample. Or possibly ScopedLoadSample
|
||||
|
||||
JobQueue
|
||||
|
||||
getLoadEvent and getLoadEventAP differ only in the style of pointer
|
||||
container which is returned. Unnecessary complexity.
|
||||
|
||||
Naming: Some names don't make sense.
|
||||
|
||||
Index
|
||||
Stop using Index to refer to keys in tables. Replace with "Key" ?
|
||||
Index implies a small integer, or a data structure.
|
||||
|
||||
This is all over the place in the Ledger API, "Index" of this and
|
||||
"Index" of that, the terminology is imprecise and helps neither
|
||||
understanding nor recall.
|
||||
|
||||
Inconsistent names
|
||||
|
||||
We have full names like SerializedType and then acronyms like STObject
|
||||
Two names for some things, e.g. SerializedLedgerEntry and SLE
|
||||
|
||||
Shared/Smart pointer typedefs in classes have a variety of different names
|
||||
for the same thing. e.g. "pointer", "ptr", "ptr_t", "wptr"
|
||||
|
||||
Verbose names
|
||||
|
||||
The prefix "Flat" is more appealing than "Serialized" because its shorter and
|
||||
easier to pronounce.
|
||||
|
||||
Ledger "Skip List"
|
||||
|
||||
Is not really a skip list data structure. This is more appropriately
|
||||
called an "index" although that name is currently used to identify hashes
|
||||
used as keys.
|
||||
|
||||
Duplicate Code
|
||||
|
||||
LedgerEntryFormat and TxFormat
|
||||
* Resolved with a todo item, create WireFormats<> template class.
|
||||
|
||||
Interfaces
|
||||
|
||||
Serializer
|
||||
|
||||
Upon analysis this class does two incompatible things. Flattening, and
|
||||
unflattening. The interface should be reimplemented as two distinct
|
||||
abstract classes, InputStream and OutputStream with suitable implementations
|
||||
such as to and from a block of memory or dynamically allocated buffer.
|
||||
|
||||
The name and conflation of dual roles serves to confuse code at the point
|
||||
of call. Does set(Serializer& s) flatten or unflatten the data? This
|
||||
would be more clear:
|
||||
bool write (OutputStream& stream);
|
||||
|
||||
We have beast for InputStream and OutputStream, we can use those now.
|
||||
|
||||
boost
|
||||
|
||||
Unclear from the class declaration what style of shared object management
|
||||
is used. Prefer to derive from a SharedObject class so that the
|
||||
behavior is explicit. Furthermore the use of intrusive containers is
|
||||
preferred over the alternative.
|
||||
|
||||
make_shared <> () is awkward.
|
||||
|
||||
boost::recursive_mutex
|
||||
|
||||
Recursive mutexes should never be necessary.
|
||||
|
||||
They require the "mutable" keyword for const members to acquire the lock (yuck)
|
||||
|
||||
Replace recursive_mutex with beast::Mutex to remove boost dependency
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Davidisms
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
(Figure out a good place to record information like this permanently)
|
||||
|
||||
Regarding a defect where a failing transaction was being submitted over and over
|
||||
again on the network (July 3, 2013)
|
||||
|
||||
The core problem was an interaction between two bits of logic.
|
||||
1) Normally, we won't relay a transaction again if we already recently relayed
|
||||
it. But this is bypassed if the transaction failed in a way that could
|
||||
allow it to succeed later. This way, if one server discovers a transaction
|
||||
can now work, it can get all servers to retry it.
|
||||
2) Normally, we won't relay a transaction if we think it can't claim a fee.
|
||||
But if we're not sure it can't claim a fee because we're in an unhealthy
|
||||
state, we propagate the transaction to let other servers decide if they
|
||||
think it can claim a fee.
|
||||
With these two bits of logic, two unhealthy servers could infinitely propagate
|
||||
a transaction back and forth between each other.
|
||||
|
||||
A node is "full below" if we believe we have (either in the database or
|
||||
scheduled to be stored in the database) the contents of every node below that
|
||||
node in a hash tree. When trying to acquire a hash tree/map, if a node is
|
||||
full below, we know not to bother with anything below that node.
|
||||
|
||||
The fullBelowCache is a cache of hashes of nodes that are full below. Which means
|
||||
there are no missing children
|
||||
|
||||
|
||||
What we want from the unique node list:
|
||||
- Some number of trusted roots (known by domain)
|
||||
probably organizations whose job is to provide a list of validators
|
||||
- We imagine the IRGA for example would establish some group whose job is to
|
||||
maintain a list of validators. There would be a public list of criteria
|
||||
that they would use to vet the validator. Things like:
|
||||
* Not anonymous
|
||||
* registered business
|
||||
* Physical location
|
||||
* Agree not to cease operations without notice / arbitrarily
|
||||
* Responsive to complaints
|
||||
- Identifiable jurisdiction
|
||||
* Homogeneity in the jurisdiction is a business risk
|
||||
* If all validators are in the same jurisdiction this is a business risk
|
||||
- OpenCoin sets criteria for the organizations
|
||||
- Rippled will ship with a list of trusted root "certificates"
|
||||
In other words this is a list of trusted domains from which the software
|
||||
can contact each trusted root and retrieve a list of "good" validators
|
||||
and then do something with that information
|
||||
- All the validation information would be public, including the broadcast
|
||||
messages.
|
||||
- The goal is to easily identify bad actors and assess network health
|
||||
* Malicious intent
|
||||
* Or, just hardware problems (faulty drive or memory)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
ChosenValidators
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
David:
|
||||
I've cut 2 of the 6 active client-facing servers to hyper. Since then, we've
|
||||
had 5 spinouts on 3 servers, none of them on the 2 I've cut over. But they
|
||||
are also the most recently restarted servers, so it's not a 100% fair test.
|
||||
|
||||
Maybe OC should have a URL that you can query to get the latest list of URI's
|
||||
for OC-approved organzations that publish lists of validators. The server and
|
||||
client can ship with that master trust URL and also the list of URI's at the
|
||||
time it's released, in case for some reason it can't pull from OC. That would
|
||||
make the default installation safe even against major changes in the
|
||||
organizations that publish validator lists.
|
||||
|
||||
The difference is that if an organization that provides lists of validators
|
||||
goes rogue, administrators don't have to act.
|
||||
|
||||
TODO:
|
||||
Write up from end-user perspective on the deployment and administration
|
||||
of this feature, on the wiki. "DRAFT" or "PROPOSE" to mark it as provisional.
|
||||
Template: https://ripple.com/wiki/Federation_protocol
|
||||
- What to do if you're a publisher of ValidatorList
|
||||
- What to do if you're a rippled administrator
|
||||
- Overview of how ChosenValidators works
|
||||
|
||||
|
||||
Goals:
|
||||
Make default configuration of rippled secure.
|
||||
* Ship with TrustedUriList
|
||||
* Also have a preset RankedValidators
|
||||
Eliminate administrative burden of maintaining
|
||||
Produce the ChosenValidators list.
|
||||
Allow quantitative analysis of network health.
|
||||
|
||||
What determines that a validator is good?
|
||||
- Are they present (i.e. sending validations)
|
||||
- Are they on the consensus ledger
|
||||
- What percentage of consensus rounds do they participate in
|
||||
- Are they stalling consensus
|
||||
* Measurements of constructive/destructive behavior is
|
||||
calculated in units of percentage of ledgers for which
|
||||
the behavior is measured.
|
||||
|
||||
Nouns
|
||||
|
||||
Validator
|
||||
- Signs ledgers and participate in consensus
|
||||
- Fields
|
||||
* Public key
|
||||
* Friendly name
|
||||
* Jurisdiction
|
||||
* Org type: profit, nonprofit, "profit/gateway"
|
||||
- Metadata
|
||||
* Visible on the network?
|
||||
* On the consensus ledger?
|
||||
* Percentage of recent participation in consensus
|
||||
* Frequency of stalling the consensus process
|
||||
|
||||
ValidatorSource
|
||||
- Abstract
|
||||
- Provides a list of Validator
|
||||
|
||||
ValidatorList
|
||||
- Essentially an array of Validator
|
||||
|
||||
TrustedUriValidatorSource
|
||||
- ValidatorSource which uses HTTPS and a predefined URI
|
||||
- Domain owner is responsible for removing bad validators
|
||||
|
||||
TrustedUriValidatorSource::List
|
||||
- Essentially an array of TrustedUriValidatorSource
|
||||
- Can be read from a file
|
||||
|
||||
LocalFileValidatorSource
|
||||
- ValidatorSource which reads information from a local file.
|
||||
|
||||
TrustedUriList // A copy of this ships with the app
|
||||
* has a KnownValidators
|
||||
|
||||
KnownValidators
|
||||
* A series of KnownValidator that comes from a TrustedUri
|
||||
* Persistent storage has a timestamp
|
||||
|
||||
RankedValidators
|
||||
* Created as the union of all KnownValidators with "weight" being the
|
||||
number of appearances.
|
||||
|
||||
ChosenValidators
|
||||
* Result of the algorithm that chooses a random subset of RankedKnownValidators
|
||||
* "local health" percentage is the percent of validations from this list that
|
||||
you've seen recently. And have they been behaving.
|
||||
|
||||
Algorithm
|
||||
|
||||
When updating a source
|
||||
51
README.md
51
README.md
@@ -1,18 +1,53 @@
|
||||
Ripple - P2P Payment Network
|
||||
============================
|
||||
#Ripple - P2P Payment Network
|
||||
|
||||
[](https://ci.ripple.com/jenkins/job/rippled/)
|
||||
|
||||
Some portions of this source code are currently closed source.
|
||||
##[](https://ci.ripple.com/jenkins/job/rippled/)
|
||||
|
||||
This is the repository for Ripple's `rippled`, reference P2P network server.
|
||||
|
||||
Build instructions:
|
||||
###Build instructions:
|
||||
* https://ripple.com/wiki/Rippled_build_instructions
|
||||
|
||||
Setup instructions:
|
||||
###Setup instructions:
|
||||
* https://ripple.com/wiki/Rippled_setup_instructions
|
||||
|
||||
For more information:
|
||||
### Repository Contents
|
||||
|
||||
#### ./bin
|
||||
Scripts and data files for Ripple integrators.
|
||||
|
||||
#### ./build
|
||||
Intermediate and final build outputs.
|
||||
|
||||
#### ./Builds
|
||||
Platform or IDE-specific project files.
|
||||
|
||||
#### ./doc
|
||||
Documentation and example configuration files.
|
||||
|
||||
#### ./src
|
||||
Source code directory. Some of the directories contained here are
|
||||
external repositories inlined via git-subtree, see the corresponding
|
||||
README for more details.
|
||||
|
||||
#### ./test
|
||||
Javascript / Mocha tests.
|
||||
|
||||
## License
|
||||
|
||||
Provided under the terms of the ISC License:
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
###For more information:
|
||||
* https://ripple.com
|
||||
* https://ripple.com/wiki
|
||||
|
||||
164
SConstruct
164
SConstruct
@@ -8,11 +8,16 @@ import glob
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
|
||||
OSX = bool(platform.mac_ver()[0])
|
||||
FreeBSD = bool('FreeBSD' == platform.system())
|
||||
Linux = bool('Linux' == platform.system())
|
||||
Ubuntu = bool(Linux and 'Ubuntu' == platform.linux_distribution()[0])
|
||||
Debian = bool(Linux and 'debian' == platform.linux_distribution()[0])
|
||||
Archlinux = bool(Linux and ('','','') == platform.linux_distribution()) #Arch still has issues with the platform module
|
||||
|
||||
USING_CLANG = OSX
|
||||
|
||||
#
|
||||
# We expect this to be set
|
||||
@@ -20,7 +25,7 @@ Ubuntu = bool(Linux and 'Ubuntu' == platform.linux_distribution()[0])
|
||||
BOOST_HOME = os.environ.get("RIPPLED_BOOST_HOME", None)
|
||||
|
||||
|
||||
if OSX or Ubuntu:
|
||||
if OSX or Ubuntu or Debian or Archlinux:
|
||||
CTAGS = 'ctags'
|
||||
elif FreeBSD:
|
||||
CTAGS = 'exctags'
|
||||
@@ -31,13 +36,14 @@ else:
|
||||
# scons tools
|
||||
#
|
||||
|
||||
HONOR_ENVS = ['CC', 'CXX', 'PATH']
|
||||
|
||||
env = Environment(
|
||||
tools = ['default', 'protoc']
|
||||
tools = ['default', 'protoc'],
|
||||
#ENV = dict((k, os.environ[k]) for k in HONOR_ENVS)
|
||||
ENV = dict((k, os.environ[k]) for k in HONOR_ENVS if k in os.environ)
|
||||
)
|
||||
|
||||
GCC_VERSION = re.split('\.', commands.getoutput(env['CXX'] + ' -dumpversion'))
|
||||
|
||||
|
||||
# Use a newer gcc on FreeBSD
|
||||
if FreeBSD:
|
||||
env.Replace(CC = 'gcc46')
|
||||
@@ -50,7 +56,18 @@ if OSX:
|
||||
env.Replace(CXX= 'clang++')
|
||||
env.Append(CXXFLAGS = ['-std=c++11', '-stdlib=libc++'])
|
||||
env.Append(LINKFLAGS='-stdlib=libc++')
|
||||
env['FRAMEWORKS'] = ['AppKit']
|
||||
env['FRAMEWORKS'] = ['AppKit','Foundation']
|
||||
|
||||
GCC_VERSION = re.split('\.', commands.getoutput(env['CXX'] + ' -dumpversion'))
|
||||
|
||||
# Add support for ccache. Usage: scons ccache=1
|
||||
ccache = ARGUMENTS.get('ccache', 0)
|
||||
if int(ccache):
|
||||
env.Prepend(CC = ['ccache'])
|
||||
env.Prepend(CXX = ['ccache'])
|
||||
ccache_dir = os.getenv('CCACHE_DIR')
|
||||
if ccache_dir:
|
||||
env.Replace(CCACHE_DIR = ccache_dir)
|
||||
|
||||
#
|
||||
# Builder for CTags
|
||||
@@ -83,13 +100,13 @@ BOOST_LIBS = [
|
||||
'boost_regex',
|
||||
'boost_system',
|
||||
'boost_thread',
|
||||
'boost_random',
|
||||
]
|
||||
|
||||
# We whitelist platforms where the non -mt version is linked with pthreads. This
|
||||
# can be verified with: ldd libboost_filesystem.* If a threading library is
|
||||
# included the platform can be whitelisted.
|
||||
if FreeBSD or Ubuntu or OSX:
|
||||
if FreeBSD or Ubuntu or Archlinux:
|
||||
# if FreeBSD or Ubuntu or Archlinux or OSX:
|
||||
# non-mt libs do link with pthreads.
|
||||
env.Append(
|
||||
LIBS = BOOST_LIBS
|
||||
@@ -101,8 +118,12 @@ else:
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# VFALCO This is my oasis of sanity. Nothing having to do with directories,
|
||||
# source files, or include paths should reside outside the boundaries.
|
||||
# VFALCO NOTE Clean area.
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Nothing having to do with directories, source files,
|
||||
# or include paths should reside outside the boundaries.
|
||||
#
|
||||
|
||||
# List of includes passed to the C++ compiler.
|
||||
@@ -110,62 +131,82 @@ else:
|
||||
#
|
||||
INCLUDE_PATHS = [
|
||||
'.',
|
||||
'build/proto',
|
||||
'Subtrees',
|
||||
'Subtrees/leveldb',
|
||||
'Subtrees/leveldb/port',
|
||||
'Subtrees/leveldb/include',
|
||||
'Subtrees/beast',
|
||||
'src',
|
||||
'src/leveldb',
|
||||
'src/leveldb/port',
|
||||
'src/leveldb/include',
|
||||
'src/beast',
|
||||
'build/proto'
|
||||
]
|
||||
|
||||
# if BOOST_HOME:
|
||||
# INCLUDE_PATHS.append(BOOST_HOME)
|
||||
|
||||
if OSX:
|
||||
COMPILED_FILES = [
|
||||
'Subtrees/beast/modules/beast_core/beast_core.mm'
|
||||
]
|
||||
else:
|
||||
COMPILED_FILES = [
|
||||
'Subtrees/beast/modules/beast_core/beast_core.cpp'
|
||||
]
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Compiled sources
|
||||
#
|
||||
|
||||
COMPILED_FILES = []
|
||||
|
||||
# -------------------
|
||||
# Beast unity sources
|
||||
#
|
||||
if OSX:
|
||||
# OSX: Use the Objective C++ version of beast_core
|
||||
COMPILED_FILES.extend (['src/ripple/beast/ripple_beastobjc.mm'])
|
||||
else:
|
||||
COMPILED_FILES.extend (['src/ripple/beast/ripple_beast.cpp'])
|
||||
COMPILED_FILES.extend (['src/ripple/beast/ripple_beastc.c'])
|
||||
|
||||
# ------------------------------
|
||||
# New-style Ripple unity sources
|
||||
#
|
||||
COMPILED_FILES.extend([
|
||||
'Subtrees/beast/modules/beast_asio/beast_asio.cpp',
|
||||
'Subtrees/beast/modules/beast_basics/beast_basics.cpp',
|
||||
# 'Subtrees/beast/modules/beast_core/beast_core.cpp',
|
||||
'Subtrees/beast/modules/beast_crypto/beast_crypto.cpp',
|
||||
'Subtrees/beast/modules/beast_db/beast_db.cpp',
|
||||
'Subtrees/beast/modules/beast_sqdb/beast_sqdb.cpp',
|
||||
'Subtrees/beast/modules/beast_sqlite/beast_sqlite.c',
|
||||
'modules/ripple_app/ripple_app_pt1.cpp',
|
||||
'modules/ripple_app/ripple_app_pt2.cpp',
|
||||
'modules/ripple_app/ripple_app_pt3.cpp',
|
||||
'modules/ripple_app/ripple_app_pt4.cpp',
|
||||
'modules/ripple_app/ripple_app_pt5.cpp',
|
||||
'modules/ripple_app/ripple_app_pt6.cpp',
|
||||
'modules/ripple_app/ripple_app_pt7.cpp',
|
||||
'modules/ripple_app/ripple_app_pt8.cpp',
|
||||
'modules/ripple_asio/ripple_asio.cpp',
|
||||
'modules/ripple_basics/ripple_basics.cpp',
|
||||
'modules/ripple_core/ripple_core.cpp',
|
||||
'modules/ripple_data/ripple_data.cpp',
|
||||
'modules/ripple_hyperleveldb/ripple_hyperleveldb.cpp',
|
||||
'modules/ripple_json/ripple_json.cpp',
|
||||
'modules/ripple_leveldb/ripple_leveldb.cpp',
|
||||
'modules/ripple_mdb/ripple_mdb.c',
|
||||
'modules/ripple_net/ripple_net.cpp',
|
||||
'modules/ripple_websocket/ripple_websocket.cpp'
|
||||
'src/ripple/common/ripple_common.cpp',
|
||||
'src/ripple/http/ripple_http.cpp',
|
||||
'src/ripple/json/ripple_json.cpp',
|
||||
'src/ripple/peerfinder/ripple_peerfinder.cpp',
|
||||
'src/ripple/resource/ripple_resource.cpp',
|
||||
'src/ripple/rocksdb/ripple_rocksdb.cpp',
|
||||
'src/ripple/rpc/ripple_rpc.cpp',
|
||||
'src/ripple/sitefiles/ripple_sitefiles.cpp',
|
||||
'src/ripple/sslutil/ripple_sslutil.cpp',
|
||||
'src/ripple/testoverlay/ripple_testoverlay.cpp',
|
||||
'src/ripple/types/ripple_types.cpp',
|
||||
'src/ripple/validators/ripple_validators.cpp'
|
||||
])
|
||||
|
||||
# ------------------------------
|
||||
# Old-style Ripple unity sources
|
||||
#
|
||||
COMPILED_FILES.extend([
|
||||
'src/ripple_app/ripple_app.cpp',
|
||||
'src/ripple_app/ripple_app_pt1.cpp',
|
||||
'src/ripple_app/ripple_app_pt2.cpp',
|
||||
'src/ripple_app/ripple_app_pt3.cpp',
|
||||
'src/ripple_app/ripple_app_pt4.cpp',
|
||||
'src/ripple_app/ripple_app_pt5.cpp',
|
||||
'src/ripple_app/ripple_app_pt6.cpp',
|
||||
'src/ripple_app/ripple_app_pt7.cpp',
|
||||
'src/ripple_app/ripple_app_pt8.cpp',
|
||||
'src/ripple_basics/ripple_basics.cpp',
|
||||
'src/ripple_core/ripple_core.cpp',
|
||||
'src/ripple_data/ripple_data.cpp',
|
||||
'src/ripple_hyperleveldb/ripple_hyperleveldb.cpp',
|
||||
'src/ripple_leveldb/ripple_leveldb.cpp',
|
||||
'src/ripple_net/ripple_net.cpp',
|
||||
'src/ripple_websocket/ripple_websocket.cpp'
|
||||
])
|
||||
|
||||
#
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# Map top level source directories to their location in the outputs
|
||||
#
|
||||
|
||||
VariantDir('build/obj/src', 'src', duplicate=0)
|
||||
VariantDir('build/obj/modules', 'modules', duplicate=0)
|
||||
VariantDir('build/obj/Subtrees', 'Subtrees', duplicate=0)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
@@ -207,18 +248,29 @@ if not OSX:
|
||||
'-rdynamic', '-pthread',
|
||||
])
|
||||
|
||||
DEBUGFLAGS = ['-g', '-DDEBUG']
|
||||
DEBUGFLAGS = ['-g', '-DDEBUG', '-D_DEBUG']
|
||||
|
||||
env.Append(CCFLAGS = ['-pthread', '-Wall', '-Wno-sign-compare', '-Wno-char-subscripts']+DEBUGFLAGS)
|
||||
env.Append(CXXFLAGS = ['-O0', '-pthread', '-Wno-invalid-offsetof', '-Wformat']+DEBUGFLAGS)
|
||||
env.Append(CXXFLAGS = ['-O1', '-pthread', '-Wno-invalid-offsetof', '-Wformat']+DEBUGFLAGS)
|
||||
|
||||
|
||||
# RTTI is required for Beast and CountedObject.
|
||||
#
|
||||
env.Append(CXXFLAGS = ['-frtti'])
|
||||
|
||||
if (int(GCC_VERSION[0]) > 4 or (int(GCC_VERSION[0]) == 4 and int(GCC_VERSION[1]) >= 7)):
|
||||
env.Append(CXXFLAGS = ['-std=c++11'])
|
||||
UBUNTU_GCC_48_INSTALL_STEPS = '''
|
||||
https://ripple.com/wiki/Ubuntu_build_instructions#Ubuntu_versions_older_than_13.10_:_Install_gcc_4.8'''
|
||||
|
||||
if not USING_CLANG:
|
||||
if (int(GCC_VERSION[0]) == 4 and int(GCC_VERSION[1]) < 7):
|
||||
print "\nrippled, using c++11, requires g++ version >= 4.8 to compile"
|
||||
|
||||
if Ubuntu:
|
||||
print UBUNTU_GCC_48_INSTALL_STEPS
|
||||
|
||||
sys.exit(1)
|
||||
else:
|
||||
env.Append(CXXFLAGS = ['-std=c++11'])
|
||||
|
||||
# FreeBSD doesn't support O_DSYNC
|
||||
if FreeBSD:
|
||||
@@ -228,7 +280,7 @@ if OSX:
|
||||
env.Append(LINKFLAGS = ['-L/usr/local/opt/openssl/lib'])
|
||||
env.Append(CXXFLAGS = ['-I/usr/local/opt/openssl/include'])
|
||||
|
||||
PROTO_SRCS = env.Protoc([], 'modules/ripple_data/protocol/ripple.proto', PROTOCOUTDIR='build/proto', PROTOCPYTHONOUTDIR=None)
|
||||
PROTO_SRCS = env.Protoc([], 'src/ripple_data/protocol/ripple.proto', PROTOCOUTDIR='build/proto', PROTOCPYTHONOUTDIR=None)
|
||||
env.Clean(PROTO_SRCS, 'site_scons/site_tools/protoc.pyc')
|
||||
|
||||
# Only tag actual Ripple files.
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# Subtrees
|
||||
|
||||
These directories come from entire outside repositories
|
||||
brought in using git-subtree.
|
||||
|
||||
About git-subtree:
|
||||
|
||||
https://github.com/apenwarr/git-subtree <br>
|
||||
http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/ <br>
|
||||
|
||||
## LevelDB
|
||||
|
||||
Ripple's fork of LevelDB is shared by the Bitcoin reference client project.
|
||||
|
||||
Repository <br>
|
||||
```
|
||||
git@github.com:ripple/LevelDB.git
|
||||
```
|
||||
Branch
|
||||
```
|
||||
ripple-fork
|
||||
```
|
||||
|
||||
## LightningDB (a.k.a. MDB)
|
||||
|
||||
A supposedly fast memory-mapped key value database system
|
||||
|
||||
Repository <br>
|
||||
```
|
||||
git://gitorious.org/mdb/mdb.git
|
||||
```
|
||||
Branch
|
||||
```
|
||||
mdb.master
|
||||
```
|
||||
|
||||
## websocket
|
||||
|
||||
Ripple's fork of websocketpp has some incompatible changes and Ripple specific includes.
|
||||
|
||||
Repository
|
||||
```
|
||||
git@github.com:ripple/websocketpp.git
|
||||
```
|
||||
Branch
|
||||
```
|
||||
ripple-fork
|
||||
```
|
||||
|
||||
## protobuf
|
||||
|
||||
Ripple's fork of protobuf doesn't have any actual changes, but since the upstream
|
||||
repository uses SVN, we have created a Git version to use with the git-subtree command.
|
||||
|
||||
Repository
|
||||
```
|
||||
git@github.com:ripple/protobuf.git
|
||||
```
|
||||
Branch
|
||||
```
|
||||
master
|
||||
```
|
||||
|
||||
**NOTE** Linux builds use the protobuf installed in /usr/lib. This will be
|
||||
fixed in a future revision.
|
||||
|
||||
## SQLite
|
||||
|
||||
Not technically a subtree but included here because it is a direct
|
||||
copy of the official SQLite distributions available here:
|
||||
|
||||
http://sqlite.org/download.html
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,103 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_BUFFERTYPE_H_INCLUDED
|
||||
#define BEAST_BUFFERTYPE_H_INCLUDED
|
||||
|
||||
/** Storage for a BufferSequence.
|
||||
|
||||
Meets these requirements:
|
||||
BufferSequence
|
||||
ConstBufferSequence (when Buffer is mutable_buffer)
|
||||
MutableBufferSequence (when Buffer is const_buffer)
|
||||
*/
|
||||
template <class Buffer>
|
||||
class BufferType
|
||||
{
|
||||
public:
|
||||
typedef Buffer value_type;
|
||||
typedef typename std::vector <Buffer>::const_iterator const_iterator;
|
||||
|
||||
BufferType ()
|
||||
: m_size (0)
|
||||
{
|
||||
}
|
||||
|
||||
template <class OtherBuffers>
|
||||
explicit BufferType (OtherBuffers const& buffers)
|
||||
: m_size (0)
|
||||
{
|
||||
m_buffers.reserve (std::distance (buffers.begin (), buffers.end ()));
|
||||
BOOST_FOREACH (typename OtherBuffers::value_type buffer, buffers)
|
||||
{
|
||||
m_size += boost::asio::buffer_size (buffer);
|
||||
m_buffers.push_back (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Determine the total size of all buffers.
|
||||
This is faster than calling boost::asio::buffer_size.
|
||||
*/
|
||||
std::size_t size () const noexcept
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
const_iterator begin () const noexcept
|
||||
{
|
||||
return m_buffers.begin ();
|
||||
}
|
||||
|
||||
const_iterator end () const noexcept
|
||||
{
|
||||
return m_buffers.end ();
|
||||
}
|
||||
|
||||
/** Retrieve a consumed BufferSequence. */
|
||||
BufferType consumed (std::size_t bytes) const
|
||||
{
|
||||
BufferType result;
|
||||
result.m_buffers.reserve (m_buffers.size ());
|
||||
BOOST_FOREACH (Buffer buffer, m_buffers)
|
||||
{
|
||||
std::size_t const have = boost::asio::buffer_size (buffer);
|
||||
std::size_t const reduce = std::min (bytes, have);
|
||||
bytes -= reduce;
|
||||
|
||||
if (have > reduce)
|
||||
result.m_buffers.push_back (buffer + reduce);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t m_size;
|
||||
std::vector <Buffer> m_buffers;
|
||||
};
|
||||
|
||||
typedef boost::asio::const_buffer ConstBuffer;
|
||||
typedef boost::asio::mutable_buffer MutableBuffer;
|
||||
|
||||
/** Meets the requirements of ConstBufferSequence */
|
||||
typedef BufferType <ConstBuffer> ConstBuffers;
|
||||
|
||||
/** Meets the requirements of MutableBufferSequence */
|
||||
typedef BufferType <MutableBuffer> MutableBuffers;
|
||||
|
||||
#endif
|
||||
@@ -1,75 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_COMPLETIONCALL_H_INCLUDED
|
||||
#define BEAST_COMPLETIONCALL_H_INCLUDED
|
||||
|
||||
// Meets these requirements:
|
||||
//
|
||||
// CompletionHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/CompletionHandler.html
|
||||
//
|
||||
class CompletionCall
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
template <class Handler>
|
||||
CompletionCall (BOOST_ASIO_MOVE_ARG(Handler) handler)
|
||||
: m_call (new CallType <Handler> (BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
||||
{
|
||||
}
|
||||
|
||||
CompletionCall (CompletionCall const& other)
|
||||
: m_call (other.m_call)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() ()
|
||||
{
|
||||
(*m_call) ();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Call : SharedObject, LeakChecked <Call>
|
||||
{
|
||||
virtual void operator() () = 0;
|
||||
};
|
||||
|
||||
template <class Handler>
|
||||
struct CallType : Call
|
||||
{
|
||||
CallType (BOOST_ASIO_MOVE_ARG(Handler) handler)
|
||||
: m_handler (handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() ()
|
||||
{
|
||||
m_handler ();
|
||||
}
|
||||
|
||||
Handler m_handler;
|
||||
};
|
||||
|
||||
private:
|
||||
SharedObjectPtr <Call> m_call;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,84 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ERRORCALL_H_INCLUDED
|
||||
#define BEAST_ERRORCALL_H_INCLUDED
|
||||
|
||||
// Meets these requirements:
|
||||
//
|
||||
// AcceptHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/AcceptHandler.html
|
||||
//
|
||||
// ConnectHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ConnectHandler.html
|
||||
//
|
||||
// ShutdownHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ShutdownHandler.html
|
||||
//
|
||||
// HandshakeHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/HandshakeHandler.html
|
||||
//
|
||||
class ErrorCall
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
template <class Handler>
|
||||
ErrorCall (BOOST_ASIO_MOVE_ARG(Handler) handler)
|
||||
: m_call (new CallType <Handler> (BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorCall (ErrorCall const& other)
|
||||
: m_call (other.m_call)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() (boost::system::error_code const& ec)
|
||||
{
|
||||
(*m_call) (ec);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Call : SharedObject, LeakChecked <Call>
|
||||
{
|
||||
virtual void operator() (boost::system::error_code const&) = 0;
|
||||
};
|
||||
|
||||
template <class Handler>
|
||||
struct CallType : Call
|
||||
{
|
||||
CallType (BOOST_ASIO_MOVE_ARG(Handler) handler)
|
||||
: m_handler (handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() (boost::system::error_code const& ec)
|
||||
{
|
||||
m_handler (ec);
|
||||
}
|
||||
|
||||
Handler m_handler;
|
||||
};
|
||||
|
||||
private:
|
||||
SharedObjectPtr <Call> m_call;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,81 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_TRANSFERCALL_H_INCLUDED
|
||||
#define BEAST_TRANSFERCALL_H_INCLUDED
|
||||
|
||||
// Meets these requirements
|
||||
//
|
||||
// ReadHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ReadHandler.html
|
||||
//
|
||||
// WriteHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/WriteHandler.html
|
||||
//
|
||||
// BUfferedHandshakeHandler
|
||||
// http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/BufferedHandshakeHandler.html
|
||||
//
|
||||
class TransferCall
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
template <class Handler>
|
||||
TransferCall (BOOST_ASIO_MOVE_ARG(Handler) handler)
|
||||
: m_call (new CallType <Handler> (BOOST_ASIO_MOVE_CAST(Handler)(handler)))
|
||||
{
|
||||
}
|
||||
|
||||
TransferCall (TransferCall const& other)
|
||||
: m_call (other.m_call)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() (boost::system::error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
(*m_call) (ec, bytes_transferred);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Call : SharedObject, LeakChecked <Call>
|
||||
{
|
||||
virtual void operator() (boost::system::error_code const&, std::size_t) = 0;
|
||||
};
|
||||
|
||||
template <class Handler>
|
||||
struct CallType : Call
|
||||
{
|
||||
CallType (BOOST_ASIO_MOVE_ARG(Handler) handler)
|
||||
: m_handler (handler)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() (boost::system::error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
m_handler (ec, bytes_transferred);
|
||||
}
|
||||
|
||||
Handler m_handler;
|
||||
};
|
||||
|
||||
private:
|
||||
SharedObjectPtr <Call> m_call;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,85 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ASIO_H_INCLUDED
|
||||
#define BEAST_ASIO_H_INCLUDED
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* If you fail to make sure that all your compile units are building Beast with
|
||||
the same set of option flags, then there's a risk that different compile
|
||||
units will treat the classes as having different memory layouts, leading to
|
||||
very nasty memory corruption errors when they all get linked together.
|
||||
That's why it's best to always include the BeastConfig.h file before any
|
||||
beast headers.
|
||||
*/
|
||||
#ifndef BEAST_BEASTCONFIG_H_INCLUDED
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ("Have you included your BeastConfig.h file before including the Beast headers?")
|
||||
# else
|
||||
# warning "Have you included your BeastConfig.h file before including the Beast headers?"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Must come before boost includes to fix the bost placeholders.
|
||||
#include "../beast_core/beast_core.h"
|
||||
|
||||
/* This module requires boost and possibly OpenSSL */
|
||||
#include "system/beast_BoostIncludes.h"
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
// Order matters
|
||||
|
||||
#include "basics/beast_PeerRole.h"
|
||||
#include "basics/beast_BufferType.h"
|
||||
#include "basics/beast_CompletionCall.h"
|
||||
#include "basics/beast_ErrorCall.h"
|
||||
#include "basics/beast_TransferCall.h"
|
||||
|
||||
#include "sockets/beast_SocketBase.h"
|
||||
#include "sockets/beast_Socket.h"
|
||||
#include "sockets/beast_SocketInterface.h"
|
||||
#include "sockets/beast_SocketWrapperBasics.h"
|
||||
#include "sockets/beast_SocketWrapper.h"
|
||||
#include "sockets/beast_SharedSocket.h"
|
||||
#include "sockets/beast_SslContext.h"
|
||||
|
||||
#include "protocol/beast_ProxyHandshake.h"
|
||||
#include "protocol/beast_HandshakeDetectorType.h"
|
||||
#include "protocol/beast_StreamHandshakeDetectorType.h"
|
||||
|
||||
#include "tests/beast_TestPeerBasics.h"
|
||||
#include "tests/beast_TestPeer.h"
|
||||
#include "tests/beast_TestPeerDetails.h"
|
||||
#include "tests/beast_TestPeerLogic.h"
|
||||
#include "tests/beast_TestPeerLogicSyncServer.h"
|
||||
#include "tests/beast_TestPeerLogicSyncClient.h"
|
||||
#include "tests/beast_TestPeerLogicProxyClient.h"
|
||||
#include "tests/beast_TestPeerLogicAsyncServer.h"
|
||||
#include "tests/beast_TestPeerLogicAsyncClient.h"
|
||||
#include "tests/beast_TestPeerType.h"
|
||||
#include "tests/beast_TestPeerDetailsTcp.h"
|
||||
#include "tests/beast_PeerTest.h"
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_HANDSHAKEDETECTORTYPE_H_INCLUDED
|
||||
#define BEAST_HANDSHAKEDETECTORTYPE_H_INCLUDED
|
||||
|
||||
class DetectPolicy
|
||||
{
|
||||
public:
|
||||
DetectPolicy ()
|
||||
: m_finished (false)
|
||||
, m_success (false)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the minimum number of bytes we need to succeed.
|
||||
*/
|
||||
virtual std::size_t needed () = 0;
|
||||
|
||||
/** Returns true if the return value of success() is valid.
|
||||
*/
|
||||
bool finished () const noexcept
|
||||
{
|
||||
return m_finished;
|
||||
}
|
||||
|
||||
/** Returns true if the buffers matched the Handshake
|
||||
*/
|
||||
bool success () const noexcept
|
||||
{
|
||||
bassert (m_finished);
|
||||
return m_success;
|
||||
}
|
||||
|
||||
protected:
|
||||
void conclude (bool success = true)
|
||||
{
|
||||
m_finished = true;
|
||||
m_success = success;
|
||||
}
|
||||
|
||||
void fail ()
|
||||
{
|
||||
conclude (false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Represents a small, fixed size buffer.
|
||||
This provides a convenient interface for doing a bytewise
|
||||
verification/reject test on a handshake protocol.
|
||||
*/
|
||||
template <int Bytes>
|
||||
struct Input
|
||||
{
|
||||
template <typename ConstBufferSequence>
|
||||
explicit Input (ConstBufferSequence const& buffer)
|
||||
: m_buffer (boost::asio::buffer (m_storage))
|
||||
, m_size (boost::asio::buffer_copy (m_buffer, buffer))
|
||||
, m_data (boost::asio::buffer_cast <uint8 const*> (m_buffer))
|
||||
{
|
||||
}
|
||||
#if 0
|
||||
uint8 const* data () const noexcept
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8 operator[] (std::size_t index) const noexcept
|
||||
{
|
||||
bassert (index >= 0 && index < m_size);
|
||||
return m_data [index];
|
||||
}
|
||||
|
||||
bool peek (std::size_t bytes) const noexcept
|
||||
{
|
||||
if (m_size >= bytes)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool peek (T* t) noexcept
|
||||
{
|
||||
std::size_t const bytes = sizeof (T);
|
||||
if (m_size >= bytes)
|
||||
{
|
||||
std::copy (m_data, m_data + bytes, t);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool consume (std::size_t bytes) noexcept
|
||||
{
|
||||
if (m_size >= bytes)
|
||||
{
|
||||
m_data += bytes;
|
||||
m_size -= bytes;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool read (T* t) noexcept
|
||||
{
|
||||
std::size_t const bytes = sizeof (T);
|
||||
if (m_size >= bytes)
|
||||
{
|
||||
//this causes a stack corruption.
|
||||
//std::copy (m_data, m_data + bytes, t);
|
||||
|
||||
memcpy (t, m_data, bytes);
|
||||
m_data += bytes;
|
||||
m_size -= bytes;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reads an integraltype in network byte order
|
||||
template <typename IntegerType>
|
||||
bool readNetworkInteger (IntegerType* value)
|
||||
{
|
||||
// Must be an integral type!
|
||||
// not available in all versions of std:: unfortunately
|
||||
//static_bassert (std::is_integral <IntegerType>::value);
|
||||
IntegerType networkValue;
|
||||
if (! read (&networkValue))
|
||||
return false;
|
||||
*value = fromNetworkByteOrder (networkValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::array <uint8, Bytes> m_storage;
|
||||
MutableBuffer m_buffer;
|
||||
std::size_t m_size;
|
||||
uint8 const* m_data;
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_finished;
|
||||
bool m_success;
|
||||
};
|
||||
|
||||
// Handshake for SSL 2
|
||||
//
|
||||
// http://tools.ietf.org/html/rfc5246#appendix-E.2
|
||||
//
|
||||
// uint8 V2CipherSpec[3];
|
||||
// struct {
|
||||
// uint16 msg_length;
|
||||
// uint8 msg_type;
|
||||
// Version version; Should be 'ProtocolVersion'?
|
||||
// uint16 cipher_spec_length;
|
||||
// uint16 session_id_length;
|
||||
// uint16 challenge_length;
|
||||
// ...
|
||||
//
|
||||
class SSL2 : public DetectPolicy
|
||||
{
|
||||
public:
|
||||
typedef int arg_type;
|
||||
|
||||
explicit SSL2 (arg_type const&)
|
||||
{
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
bytesNeeded = 3
|
||||
};
|
||||
|
||||
std::size_t needed ()
|
||||
{
|
||||
return bytesNeeded;
|
||||
}
|
||||
|
||||
template <typename ConstBufferSequence>
|
||||
void analyze (ConstBufferSequence const& buffer)
|
||||
{
|
||||
Input <bytesNeeded> in (buffer);
|
||||
|
||||
{
|
||||
uint8 byte;
|
||||
if (! in.peek (&byte))
|
||||
return;
|
||||
|
||||
// First byte must have the high bit set
|
||||
//
|
||||
if((byte & 0x80) != 0x80)
|
||||
return fail ();
|
||||
}
|
||||
|
||||
// The remaining bits contain the
|
||||
// length of the following data in bytes.
|
||||
//
|
||||
uint16 msg_length;
|
||||
if (! in.readNetworkInteger(&msg_length))
|
||||
return;
|
||||
|
||||
// sizeof (msg_type +
|
||||
// Version (ProtcolVersion?) +
|
||||
// cipher_spec_length +
|
||||
// session_id_length +
|
||||
// challenge_length)
|
||||
//
|
||||
// Should be 9 or greater.
|
||||
//
|
||||
if (msg_length < 9)
|
||||
return fail ();
|
||||
|
||||
uint8 msg_type;
|
||||
if (! in.read (&msg_type))
|
||||
return;
|
||||
|
||||
// The msg_type must be 0x01 for a version 2 ClientHello
|
||||
//
|
||||
if (msg_type != 0x01)
|
||||
return fail ();
|
||||
|
||||
conclude ();
|
||||
}
|
||||
};
|
||||
|
||||
// Handshake for SSL 3 (Also TLS 1.0 and 1.1)
|
||||
//
|
||||
// http://www.ietf.org/rfc/rfc2246.txt
|
||||
//
|
||||
// Section 7.4. Handshake protocol
|
||||
//
|
||||
class SSL3 : public DetectPolicy
|
||||
{
|
||||
public:
|
||||
typedef int arg_type; // dummy
|
||||
|
||||
explicit SSL3 (arg_type const&)
|
||||
{
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
bytesNeeded = 6
|
||||
};
|
||||
|
||||
std::size_t needed ()
|
||||
{
|
||||
return bytesNeeded;
|
||||
}
|
||||
|
||||
template <typename ConstBufferSequence>
|
||||
void analyze (ConstBufferSequence const& buffer)
|
||||
{
|
||||
uint16 version;
|
||||
Input <bytesNeeded> in (buffer);
|
||||
|
||||
uint8 msg_type;
|
||||
if (! in.read (&msg_type))
|
||||
return;
|
||||
|
||||
// msg_type must be 0x16 = "SSL Handshake"
|
||||
//
|
||||
if (msg_type != 0x16)
|
||||
return fail ();
|
||||
|
||||
if (! in.read (&version))
|
||||
return;
|
||||
version = fromNetworkByteOrder (version);
|
||||
|
||||
uint16 length;
|
||||
if (! in.read (&length))
|
||||
return;
|
||||
|
||||
length = fromNetworkByteOrder (length);
|
||||
|
||||
conclude ();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <typename Logic>
|
||||
class HandshakeDetectorType
|
||||
{
|
||||
public:
|
||||
typedef typename Logic::arg_type arg_type;
|
||||
|
||||
explicit HandshakeDetectorType (arg_type const& arg = arg_type ())
|
||||
: m_logic (arg)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t needed () noexcept
|
||||
{
|
||||
return m_logic.needed ();
|
||||
}
|
||||
|
||||
bool finished () noexcept
|
||||
{
|
||||
return m_logic.finished ();
|
||||
}
|
||||
|
||||
/** If finished is true, this tells us if the handshake was detected.
|
||||
*/
|
||||
bool success () noexcept
|
||||
{
|
||||
return m_logic.success ();
|
||||
}
|
||||
|
||||
/** Analyze the buffer to match the Handshake.
|
||||
Returns `true` if the analysis is complete.
|
||||
*/
|
||||
template <typename ConstBufferSequence>
|
||||
bool analyze (ConstBufferSequence const& buffer)
|
||||
{
|
||||
bassert (! m_logic.finished ());
|
||||
m_logic.analyze (buffer);
|
||||
return m_logic.finished ();
|
||||
}
|
||||
|
||||
private:
|
||||
Logic m_logic;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,384 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ProxyHandshake::ProxyHandshake (bool expectHandshake)
|
||||
: m_status (expectHandshake ? statusHandshake : statusNone)
|
||||
, m_gotCR (false)
|
||||
{
|
||||
m_buffer.preallocateBytes (maxVersion1Bytes);
|
||||
}
|
||||
|
||||
ProxyHandshake::~ProxyHandshake ()
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t ProxyHandshake::feed (void const* inputBuffer, size_t inputBytes)
|
||||
{
|
||||
std::size_t bytesConsumed = 0;
|
||||
|
||||
char const* p = static_cast <char const*> (inputBuffer);
|
||||
|
||||
if (m_status == statusHandshake)
|
||||
{
|
||||
if (! m_gotCR)
|
||||
{
|
||||
while (inputBytes > 0 && m_buffer.length () < maxVersion1Bytes - 1)
|
||||
{
|
||||
beast_wchar c = *p++;
|
||||
++bytesConsumed;
|
||||
--inputBytes;
|
||||
m_buffer += c;
|
||||
|
||||
if (c == '\r')
|
||||
{
|
||||
m_gotCR = true;
|
||||
break;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
m_status = statusFailed;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_buffer.length () > maxVersion1Bytes - 1)
|
||||
{
|
||||
m_status = statusFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_status == statusHandshake)
|
||||
{
|
||||
if (inputBytes > 0 && m_gotCR)
|
||||
{
|
||||
bassert (m_buffer.length () < maxVersion1Bytes);
|
||||
|
||||
char const lf ('\n');
|
||||
|
||||
if (*p == lf)
|
||||
{
|
||||
++bytesConsumed;
|
||||
--inputBytes;
|
||||
m_buffer += lf;
|
||||
|
||||
parseLine ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = statusFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytesConsumed;
|
||||
}
|
||||
|
||||
void ProxyHandshake::parseLine ()
|
||||
{
|
||||
Version1 p;
|
||||
|
||||
bool success = p.parse (m_buffer.getCharPointer (), m_buffer.length ());
|
||||
|
||||
if (success)
|
||||
{
|
||||
m_endpoints = p.endpoints;
|
||||
m_status = statusOk;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = statusFailed;
|
||||
}
|
||||
}
|
||||
|
||||
int ProxyHandshake::indexOfFirstNonNumber (String const& input)
|
||||
{
|
||||
bassert (input.length () > 0);
|
||||
|
||||
int i = 0;
|
||||
for (; i < input.length (); ++i)
|
||||
{
|
||||
if (! CharacterFunctions::isDigit (input [i]))
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
bool ProxyHandshake::chop (String const& what, String& input)
|
||||
{
|
||||
if (input.startsWith (what))
|
||||
{
|
||||
input = input.substring (what.length ());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProxyHandshake::chopUInt (int* value, int limit, String& input)
|
||||
{
|
||||
if (input.length () <= 0)
|
||||
return false;
|
||||
|
||||
String const s = input.substring (0, indexOfFirstNonNumber (input));
|
||||
|
||||
if (s.length () <= 0)
|
||||
return false;
|
||||
|
||||
int const n = s.getIntValue ();
|
||||
|
||||
// Leading zeroes disallowed as per spec, to prevent confusion with octal
|
||||
if (String (n) != s)
|
||||
return false;
|
||||
|
||||
if (n < 0 || n > limit)
|
||||
return false;
|
||||
|
||||
input = input.substring (s.length ());
|
||||
|
||||
*value = n;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
|
||||
steps:
|
||||
|
||||
Proxy protocol lets us filter attackers by learning the source ip and port
|
||||
|
||||
1. Determine if we should use the proxy on a connection
|
||||
- Port just for proxy protocol connections
|
||||
- Filter on source IPs
|
||||
|
||||
2. Read a line from the connection to get the proxy information
|
||||
|
||||
3. Parse the line (human or binary?)
|
||||
|
||||
4. Code Interface to retrieve proxy information (ip/port) on connection
|
||||
|
||||
*/
|
||||
|
||||
ProxyHandshake::Version1::Version1 ()
|
||||
{
|
||||
}
|
||||
|
||||
bool ProxyHandshake::IPv4::Addr::chop (String& input)
|
||||
{
|
||||
if (!ProxyHandshake::chopUInt (&a, 255, input))
|
||||
return false;
|
||||
|
||||
if (!ProxyHandshake::chop (".", input))
|
||||
return false;
|
||||
|
||||
if (!ProxyHandshake::chopUInt (&b, 255, input))
|
||||
return false;
|
||||
|
||||
if (!ProxyHandshake::chop (".", input))
|
||||
return false;
|
||||
|
||||
if (!ProxyHandshake::chopUInt (&c, 255, input))
|
||||
return false;
|
||||
|
||||
if (!ProxyHandshake::chop (".", input))
|
||||
return false;
|
||||
|
||||
if (!ProxyHandshake::chopUInt (&d, 255, input))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProxyHandshake::Version1::parse (void const* headerData, size_t headerBytes)
|
||||
{
|
||||
String input (static_cast <CharPointer_UTF8::CharType const*> (headerData), headerBytes);
|
||||
|
||||
if (input.length () < 2)
|
||||
return false;
|
||||
|
||||
if (! input.endsWith ("\r\n"))
|
||||
return false;
|
||||
|
||||
input = input.dropLastCharacters (2);
|
||||
|
||||
if (! ProxyHandshake::chop ("PROXY ", input))
|
||||
return false;
|
||||
|
||||
if (ProxyHandshake::chop ("UNKNOWN", input))
|
||||
{
|
||||
endpoints.proto = protoUnknown;
|
||||
|
||||
input = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ProxyHandshake::chop ("TCP4 ", input))
|
||||
{
|
||||
endpoints.proto = protoTcp4;
|
||||
|
||||
if (! endpoints.ipv4.sourceAddr.chop (input))
|
||||
return false;
|
||||
|
||||
if (! ProxyHandshake::chop (" ", input))
|
||||
return false;
|
||||
|
||||
if (! endpoints.ipv4.destAddr.chop (input))
|
||||
return false;
|
||||
|
||||
if (! ProxyHandshake::chop (" ", input))
|
||||
return false;
|
||||
|
||||
if (! ProxyHandshake::chopUInt (&endpoints.ipv4.sourcePort, 65535, input))
|
||||
return false;
|
||||
|
||||
if (! ProxyHandshake::chop (" ", input))
|
||||
return false;
|
||||
|
||||
if (! ProxyHandshake::chopUInt (&endpoints.ipv4.destPort, 65535, input))
|
||||
return false;
|
||||
}
|
||||
else if (ProxyHandshake::chop ("TCP6 ", input))
|
||||
{
|
||||
endpoints.proto = protoTcp6;
|
||||
|
||||
//bassertfalse;
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Can't have anything extra between the last port number and the CRLF
|
||||
if (input.length () > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ProxyHandshakeTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ProxyHandshakeTests () : UnitTest ("ProxyHandshake", "beast")
|
||||
{
|
||||
}
|
||||
|
||||
static std::string goodIpv4 ()
|
||||
{
|
||||
return "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"; // 56 chars
|
||||
}
|
||||
|
||||
static std::string goodIpv6 ()
|
||||
{
|
||||
return "PROXY TCP6 fffffffffffffffffffffffffffffffffffffff.fffffffffffffffffffffffffffffffffffffff 65535 65535\r\n";
|
||||
//1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123 4 (104 chars)
|
||||
}
|
||||
|
||||
static std::string goodUnknown ()
|
||||
{
|
||||
return "PROXY UNKNOWN\r\n";
|
||||
}
|
||||
|
||||
static std::string goodUnknownBig ()
|
||||
{
|
||||
return "PROXY UNKNOWN fffffffffffffffffffffffffffffffffffffff.fffffffffffffffffffffffffffffffffffffff 65535 65535\r\n";
|
||||
//1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 7 (107 chars)
|
||||
}
|
||||
|
||||
void testHandshake (std::string const& s, bool shouldSucceed)
|
||||
{
|
||||
if (s.size () > 1)
|
||||
{
|
||||
ProxyHandshake h (true);
|
||||
|
||||
expect (h.getStatus () == ProxyHandshake::statusHandshake);
|
||||
|
||||
for (std::size_t i = 0; i < s.size () && h.getStatus () == ProxyHandshake::statusHandshake ; ++i)
|
||||
{
|
||||
std::size_t const bytesConsumed = h.feed (& s[i], 1);
|
||||
|
||||
if (i != s.size () - 1)
|
||||
expect (h.getStatus () == ProxyHandshake::statusHandshake);
|
||||
|
||||
expect (bytesConsumed == 1);
|
||||
}
|
||||
|
||||
if (shouldSucceed)
|
||||
{
|
||||
expect (h.getStatus () == ProxyHandshake::statusOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
expect (h.getStatus () == ProxyHandshake::statusFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
void testVersion1String (std::string const& s, bool shouldSucceed)
|
||||
{
|
||||
ProxyHandshake::Version1 p;
|
||||
|
||||
if (shouldSucceed)
|
||||
{
|
||||
expect (p.parse (s.c_str (), s.size ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected (p.parse (s.c_str (), s.size ()));
|
||||
}
|
||||
|
||||
for (std::size_t i = 1; i < s.size () - 1; ++i)
|
||||
{
|
||||
String const partial = String (s).dropLastCharacters (i);
|
||||
std::string ss (partial.toStdString ());
|
||||
|
||||
expect (! p.parse (ss.c_str (), ss.size ()));
|
||||
}
|
||||
|
||||
testHandshake (s, shouldSucceed);
|
||||
}
|
||||
|
||||
void testVersion1 ()
|
||||
{
|
||||
beginTestCase ("version1");
|
||||
|
||||
testVersion1String (goodIpv4 (), true);
|
||||
testVersion1String (goodIpv6 (), false);
|
||||
testVersion1String (goodUnknown (), true);
|
||||
testVersion1String (goodUnknownBig (), true);
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
testVersion1 ();
|
||||
}
|
||||
};
|
||||
|
||||
static ProxyHandshakeTests proxyHandshakeTests;
|
||||
@@ -1,164 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_PROXYYHANDSHAKE_H_INCLUDED
|
||||
#define BEAST_PROXYYHANDSHAKE_H_INCLUDED
|
||||
|
||||
/** PROXY protocol handshake state machine.
|
||||
|
||||
The PROXY Protocol:
|
||||
http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
|
||||
*/
|
||||
class ProxyHandshake
|
||||
{
|
||||
public:
|
||||
/** Status of the handshake state machine. */
|
||||
enum Status
|
||||
{
|
||||
statusNone, // No handshake expected
|
||||
statusHandshake, // Handshake in progress
|
||||
statusFailed, // Handshake failed
|
||||
statusOk, // Handshake succeeded
|
||||
};
|
||||
|
||||
enum Proto
|
||||
{
|
||||
protoTcp4,
|
||||
protoTcp6,
|
||||
protoUnknown
|
||||
};
|
||||
|
||||
/** PROXY information for IPv4 families. */
|
||||
struct IPv4
|
||||
{
|
||||
struct Addr
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
|
||||
bool chop (String& input);
|
||||
};
|
||||
|
||||
Addr sourceAddr;
|
||||
Addr destAddr;
|
||||
int sourcePort;
|
||||
int destPort;
|
||||
};
|
||||
|
||||
/** PROXY information for IPv6 families. */
|
||||
struct IPv6
|
||||
{
|
||||
struct Addr
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
};
|
||||
|
||||
Addr sourceAddr;
|
||||
Addr destAddr;
|
||||
int sourcePort;
|
||||
int destPort;
|
||||
};
|
||||
|
||||
/** Fully decoded PROXY information. */
|
||||
struct Endpoints
|
||||
{
|
||||
Endpoints ()
|
||||
: proto (protoUnknown)
|
||||
{
|
||||
}
|
||||
|
||||
Proto proto;
|
||||
IPv4 ipv4; // valid if proto == protoTcp4
|
||||
IPv6 ipv6; // valid if proto == protoTcp6;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Parser for PROXY version 1. */
|
||||
struct Version1
|
||||
{
|
||||
enum
|
||||
{
|
||||
// Maximum input buffer size needed, including a null
|
||||
// terminator, as per the PROXY protocol specification.
|
||||
maxBufferBytes = 108
|
||||
};
|
||||
|
||||
Endpoints endpoints;
|
||||
|
||||
Version1 ();
|
||||
|
||||
/** Parse the header.
|
||||
@param rawHeader a pointer to the header data
|
||||
@return `true` If it was parsed successfully.
|
||||
*/
|
||||
bool parse (void const* headerData, size_t headerBytes);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Create the handshake state.
|
||||
If a handshake is expected, then it is required.
|
||||
@param wantHandshake `false` to skip handshaking.
|
||||
*/
|
||||
explicit ProxyHandshake (bool expectHandshake = false);
|
||||
|
||||
~ProxyHandshake ();
|
||||
|
||||
inline Status getStatus () const noexcept
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
inline Endpoints const& getEndpoints () const noexcept
|
||||
{
|
||||
return m_endpoints;
|
||||
};
|
||||
|
||||
/** Feed the handshaking state engine.
|
||||
@return The number of bytes consumed in the input buffer.
|
||||
*/
|
||||
std::size_t feed (void const* inputBuffer, std::size_t inputBytes);
|
||||
|
||||
// Utility functions used by parsers
|
||||
static int indexOfFirstNonNumber (String const& input);
|
||||
static bool chop (String const& what, String& input);
|
||||
static bool chopUInt (int* value, int limit, String& input);
|
||||
|
||||
private:
|
||||
void parseLine ();
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
maxVersion1Bytes = 107 // including crlf, not including null term
|
||||
};
|
||||
|
||||
Status m_status;
|
||||
String m_buffer;
|
||||
bool m_gotCR;
|
||||
Endpoints m_endpoints;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,78 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_STREAMHANDSHAKEDETECTORTYPE_H_INCLUDED
|
||||
#define BEAST_STREAMHANDSHAKEDETECTORTYPE_H_INCLUDED
|
||||
|
||||
/** Wraps a HandshakeDetector and does the work on the Socket for you.
|
||||
*/
|
||||
template <class Detector>
|
||||
class StreamHandshakeDetectorType
|
||||
{
|
||||
protected:
|
||||
typedef boost::system::error_code error_code;
|
||||
typedef StreamHandshakeDetectorType <Detector> This;
|
||||
|
||||
public:
|
||||
typedef typename Detector::arg_type arg_type;
|
||||
|
||||
explicit StreamHandshakeDetectorType (arg_type const& arg = arg_type ())
|
||||
{
|
||||
}
|
||||
|
||||
template <typename HandshakeHandler>
|
||||
void async_handshake (Socket& socket, BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler)
|
||||
{
|
||||
#if 0
|
||||
std::size_t const bytes = m_detector.needed ();
|
||||
#if 1
|
||||
boost::asio::async_read (socket, m_buffer.prepare (bytes), boost::bind (
|
||||
&This::on_read <typename HandshakeHandler>, this, &socket,
|
||||
handler,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
#else
|
||||
boost::asio::async_read (socket, m_buffer.prepare (bytes), boost::bind (
|
||||
&This::on_read2, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename HandshakeHandler>
|
||||
void on_read (Socket* socket, BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler,
|
||||
error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
m_buffer.commit (bytes_transferred);
|
||||
|
||||
if (m_detector.analyze (m_buffer.data ()))
|
||||
{
|
||||
if (m_detector.success ())
|
||||
{
|
||||
//socket->async_handshake (Socket::server, m_buffer.data (), handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Detector m_detector;
|
||||
boost::asio::streambuf m_buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,83 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SHAREDSOCKET_H_INCLUDED
|
||||
#define BEAST_SHAREDSOCKET_H_INCLUDED
|
||||
|
||||
/** A Socket interface with reference counting.
|
||||
|
||||
You can keep a pointer to the base class so that you don't have
|
||||
to see the template or underlying object implementation.
|
||||
|
||||
@see SharedSocketTYpe, SharedObjectPtr
|
||||
*/
|
||||
/** @{ */
|
||||
class SharedSocket
|
||||
: public SharedObject
|
||||
, public virtual Socket
|
||||
{
|
||||
public:
|
||||
/** Store your SharedSocket in one of these! */
|
||||
typedef SharedObjectPtr <SharedSocket> Ptr;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A RAII container for wrapping an object as a Socket.
|
||||
|
||||
To use this, construct the class with an instance of your object
|
||||
created with operator new. The constructor will take ownership,
|
||||
and delete it when the last reference is removed. For example:
|
||||
|
||||
@code
|
||||
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ssl:context ctx;
|
||||
|
||||
SharedSocket::Ptr mySocket (
|
||||
new (boost::asio::ssl::stream (ios, ctx)));
|
||||
|
||||
mySocket->handshake ();
|
||||
|
||||
@endcode
|
||||
|
||||
@see SharedSocket
|
||||
*/
|
||||
template <class Object>
|
||||
class SharedSocketType
|
||||
: public SharedSocket
|
||||
, public SocketWrapper <Object>
|
||||
{
|
||||
public:
|
||||
/** Create the shared socket.
|
||||
This takes posession of the object, which will be deleted
|
||||
when the last reference goes away.
|
||||
*/
|
||||
SharedSocketType (Object* object)
|
||||
: SocketWrapper <Object> (object)
|
||||
, m_object (object)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPointer <Object> m_object;
|
||||
};
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
@@ -1,277 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Socket::~Socket ()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// basic_io_object
|
||||
//
|
||||
|
||||
boost::asio::io_service& Socket::get_io_service ()
|
||||
{
|
||||
pure_virtual ();
|
||||
return *static_cast <boost::asio::io_service*>(nullptr);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// basic_socket
|
||||
//
|
||||
|
||||
void* Socket::lowest_layer (char const*) const
|
||||
{
|
||||
pure_virtual ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* Socket::native_handle (char const*) const
|
||||
{
|
||||
pure_virtual ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
boost::system::error_code Socket::cancel (boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
boost::system::error_code Socket::shutdown (shutdown_type, boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
boost::system::error_code Socket::close (boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// basic_socket_acceptor
|
||||
//
|
||||
|
||||
boost::system::error_code Socket::accept (Socket&, boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (boost::system::error_code))
|
||||
Socket::async_accept (Socket&, BOOST_ASIO_MOVE_ARG(ErrorCall) handler)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
ErrorCall, void (boost::system::error_code)> init(
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
return init.result.get();
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// basic_stream_socket
|
||||
//
|
||||
|
||||
std::size_t Socket::read_some (MutableBuffers const&, boost::system::error_code& ec)
|
||||
{
|
||||
pure_virtual (ec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t Socket::write_some (ConstBuffers const&, boost::system::error_code& ec)
|
||||
{
|
||||
pure_virtual (ec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (boost::system::error_code, std::size_t))
|
||||
Socket::async_read_some (MutableBuffers const&, BOOST_ASIO_MOVE_ARG(TransferCall) handler)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
TransferCall, void (boost::system::error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
return init.result.get();
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (boost::system::error_code, std::size_t))
|
||||
Socket::async_write_some (ConstBuffers const&, BOOST_ASIO_MOVE_ARG(TransferCall) handler)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
TransferCall, void (boost::system::error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
return init.result.get();
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// ssl::stream
|
||||
//
|
||||
|
||||
bool Socket::requires_handshake ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::system::error_code Socket::handshake (handshake_type, boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (boost::system::error_code))
|
||||
Socket::async_handshake (handshake_type, BOOST_ASIO_MOVE_ARG(ErrorCall) handler)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
ErrorCall, void (boost::system::error_code)> init(
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
return init.result.get();
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BEAST_ASIO_HAS_BUFFEREDHANDSHAKE
|
||||
|
||||
boost::system::error_code Socket::handshake (handshake_type,
|
||||
ConstBuffers const&, boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (boost::system::error_code, std::size_t))
|
||||
Socket::async_handshake (handshake_type, ConstBuffers const&,
|
||||
BOOST_ASIO_MOVE_ARG(TransferCall) handler)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
TransferCall, void (boost::system::error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
return init.result.get();
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
boost::system::error_code Socket::shutdown (boost::system::error_code& ec)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (boost::system::error_code))
|
||||
Socket::async_shutdown (BOOST_ASIO_MOVE_ARG(ErrorCall) handler)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
ErrorCall, void (boost::system::error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
return init.result.get();
|
||||
#else
|
||||
boost::system::error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* members, and the most common base class in which they appear:
|
||||
|
||||
basic_io_object
|
||||
io_service& get_io_service ()
|
||||
|
||||
basic_socket <Protocol> : basic_io_object
|
||||
typedef protocol_type
|
||||
typedef lowest_layer_type
|
||||
|
||||
lowest_layer_type& lowest_layer ()
|
||||
lowest_layer_type const& lowest_layer () const
|
||||
native_handle ()
|
||||
cancel ()
|
||||
shutdon (shutdown_type)
|
||||
close ()
|
||||
<various>
|
||||
|
||||
basic_socket_acceptor <Protocol> : basic_io_object
|
||||
typedef protocol_type
|
||||
native_handle ()
|
||||
listen ()
|
||||
accept ()
|
||||
async_accept ()
|
||||
cancel ()
|
||||
close ()
|
||||
|
||||
basic_stream_socket <Protocol> : basic_socket <Protocol>
|
||||
|
||||
ssl::stream
|
||||
handshake ()
|
||||
async_handshake ()
|
||||
shutdown ()
|
||||
*/
|
||||
@@ -1,62 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SOCKETINTERFACE_H_INCLUDED
|
||||
#define BEAST_SOCKETINTERFACE_H_INCLUDED
|
||||
|
||||
/** These define the interfaces that SocketWrapper can adapt with SFINAE. */
|
||||
struct SocketInterface
|
||||
{
|
||||
// has close()
|
||||
struct Close { };
|
||||
|
||||
/** Tag for some compatibility with boost::asio::basic_socket_acceptor
|
||||
http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/basic_socket_acceptor.html
|
||||
*/
|
||||
struct Acceptor : Close { };
|
||||
|
||||
// Has lowest_layer() and lowest_layer_type
|
||||
struct LowestLayer { };
|
||||
|
||||
/** Tag for parts of boost::asio::basic_socket
|
||||
http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/basic_socket.html
|
||||
*/
|
||||
struct Socket : Close, LowestLayer { };
|
||||
|
||||
/** Tag for parts of boost::asio::basic_stream_socket
|
||||
http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/basic_stream_socket.html
|
||||
*/
|
||||
struct SyncStream { };
|
||||
struct AsyncStream { };
|
||||
struct Stream : SyncStream, AsyncStream { };
|
||||
|
||||
/** Tags for parts of boost::asio::ssl::stream
|
||||
http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/ssl__stream.html
|
||||
*/
|
||||
struct AnyHandshake { };
|
||||
struct SyncHandshake : AnyHandshake { };
|
||||
struct AsyncHandshake : AnyHandshake { };
|
||||
struct BufferedSyncHandshake : AnyHandshake { };
|
||||
struct BufferedAsyncHandshake : AnyHandshake { };
|
||||
struct Handshake : SyncHandshake, AsyncHandshake,
|
||||
BufferedSyncHandshake, BufferedAsyncHandshake,
|
||||
LowestLayer { };
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,729 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SOCKETWRAPPER_H_INCLUDED
|
||||
#define BEAST_SOCKETWRAPPER_H_INCLUDED
|
||||
|
||||
/** Wraps a reference to any object and exports all availble interfaces.
|
||||
|
||||
If the object does not support an interface, calling those
|
||||
member functions will behave as if a pure virtual was called.
|
||||
|
||||
Note that only a reference to the underlying is stored. Management
|
||||
of the lifetime of the object is controlled by the caller.
|
||||
|
||||
Examples of the type of WrappedObject:
|
||||
|
||||
asio::ip::tcp::socket
|
||||
arg must be an io_context
|
||||
SocketWrapper will create and take ownership of the tcp::socket
|
||||
WrappedObjectType will be tcp::socket
|
||||
next_layer () returns a asio::ip::tcp::socket&
|
||||
lowest_layer () returns a asio::ip::tcp::socket&
|
||||
|
||||
asio::ip::tcp::socket&
|
||||
arg must be an existing socket&
|
||||
The caller owns the underlying socket object
|
||||
WrappedObjectType will be tcp::socket
|
||||
next_layer () returns a asio::ip::tcp::socket&
|
||||
lowest_layer () returns a asio::ip::tcp::socket&
|
||||
|
||||
asio::ssl::stream <asio::ip::tcp::socket>
|
||||
arg must be an io_context
|
||||
SocketWrapper creates and takes ownership of the ssl::stream
|
||||
WrappedObjecType will be asio::ssl::stream <asio::ip::tcp::socket>
|
||||
next_layer () returns a asio::ip::tcp::socket&
|
||||
lowest_layer () returns a asio::ip::tcp::socket&
|
||||
|
||||
asio::ssl::stream <asio::ip::tcp::socket&>
|
||||
arg must be an existing socket&
|
||||
The caller owns the socket, but SocketWrapper owns the ssl::stream
|
||||
WrappedObjectType will be asio::ssl::stream <asio::ip::tcp::socket&>
|
||||
next_layer () returns a asio::ip::tcp::socket&
|
||||
lowest_layer () returns a asio::ip::tcp::socket&
|
||||
|
||||
asio::ssl::stream <asio::buffered_stream <asio::ip::tcp::socket> > >
|
||||
This makes my head explode
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace SocketWrapperMemberChecks
|
||||
{
|
||||
template <bool Enable>
|
||||
struct EnableIf : boost::false_type { };
|
||||
|
||||
template <>
|
||||
struct EnableIf <true> : boost::true_type { };
|
||||
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_get_io_service, get_io_service);
|
||||
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_lowest_layer, lowest_layer);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_cancel, cancel);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_shutdown, shutdown);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_close, close);
|
||||
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_accept, accept);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_async_accept, async_accept);
|
||||
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_read_some, read_some);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_write_some, write_some);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_async_read_some, async_read_some);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_async_write_some, async_write_some);
|
||||
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_handshake, handshake);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_async_handshake, async_handshake);
|
||||
BEAST_DEFINE_IS_CALL_POSSIBLE(has_async_shutdown, async_shutdown);
|
||||
|
||||
// Extracts the underlying socket type from the protocol of another asio object
|
||||
template <typename T, typename Enable = void>
|
||||
struct native_socket
|
||||
{
|
||||
typedef void* socket_type;
|
||||
inline native_socket (Socket&) : m_socket (nullptr) { SocketBase::pure_virtual (); }
|
||||
inline socket_type& get () { SocketBase::pure_virtual (); return m_socket; }
|
||||
inline socket_type& operator-> () { return get (); }
|
||||
private:
|
||||
socket_type m_socket;
|
||||
};
|
||||
|
||||
// Enabled if T::protocol_type::socket exists as a type
|
||||
template <typename T>
|
||||
struct native_socket <T, typename boost::enable_if <boost::is_class <
|
||||
typename T::protocol_type::socket> >::type>
|
||||
{
|
||||
typedef typename T::protocol_type::socket socket_type;
|
||||
inline native_socket (Socket& peer) : m_socket_ptr (&peer.native_handle <socket_type> ()) { }
|
||||
inline socket_type& get () noexcept { return *m_socket_ptr; }
|
||||
inline socket_type& operator-> () noexcept { return get (); }
|
||||
private:
|
||||
socket_type* m_socket_ptr;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename WrappedObject>
|
||||
class SocketWrapper
|
||||
: public virtual Socket
|
||||
, public SocketWrapperBasics
|
||||
, public Uncopyable
|
||||
{
|
||||
private:
|
||||
typedef typename boost::remove_reference <WrappedObject>::type wrapped_type;
|
||||
|
||||
public:
|
||||
typedef typename boost::remove_reference <WrappedObject>::type WrappedObjectType;
|
||||
|
||||
template <typename Arg>
|
||||
explicit SocketWrapper (Arg& arg)
|
||||
: m_object (arg)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2>
|
||||
SocketWrapper (Arg1& arg1, Arg2& arg2)
|
||||
: m_object (arg1, arg2)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// basic_io_object
|
||||
//
|
||||
|
||||
boost::asio::io_service& get_io_service ()
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
#if 0
|
||||
// This is the one that doesn't work, (void) arg lists
|
||||
return get_io_service (
|
||||
EnableIf <has_get_io_service <wrapped_type,
|
||||
io_service ()>::value> ());
|
||||
#else
|
||||
return get_io_service (boost::true_type ());
|
||||
#endif
|
||||
}
|
||||
|
||||
boost::asio::io_service& get_io_service (
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.get_io_service ();
|
||||
}
|
||||
|
||||
boost::asio::io_service& get_io_service (
|
||||
boost::false_type)
|
||||
{
|
||||
pure_virtual ();
|
||||
return *static_cast <boost::asio::io_service*>(nullptr);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// basic_socket
|
||||
//
|
||||
|
||||
#if 0
|
||||
// This is a potential work-around for the problem with
|
||||
// the has_type_lowest_layer_type template, but requires
|
||||
// Boost 1.54 or later.
|
||||
//
|
||||
// This include will be needed:
|
||||
//
|
||||
// #include <boost/tti/has_type.hpp>
|
||||
//
|
||||
//
|
||||
BOOST_TTI_HAS_TYPE(lowest_layer_type)
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct has_type_lowest_layer_type
|
||||
{
|
||||
typedef char yes;
|
||||
typedef struct {char dummy[2];} no;
|
||||
template <class C> static yes f(typename C::lowest_layer_type*);
|
||||
template <class C> static no f(...);
|
||||
#ifdef _MSC_VER
|
||||
static bool const value = sizeof(f<T>(0)) == 1;
|
||||
#else
|
||||
// This line fails to compile under Visual Studio 2012
|
||||
static bool const value = sizeof(has_type_lowest_layer_type<T>::f<T>(0)) == 1;
|
||||
#endif
|
||||
};
|
||||
|
||||
void* lowest_layer (char const* type_name) const
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return lowest_layer (type_name,
|
||||
EnableIf <has_type_lowest_layer_type <wrapped_type>::value> ());
|
||||
}
|
||||
|
||||
void* lowest_layer (char const* type_name,
|
||||
boost::true_type) const
|
||||
{
|
||||
char const* const name (typeid (typename wrapped_type::lowest_layer_type).name ());
|
||||
if (strcmp (name, type_name) == 0)
|
||||
return const_cast <void*> (static_cast <void const*> (&m_object.lowest_layer ()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* lowest_layer (char const*,
|
||||
boost::false_type) const
|
||||
{
|
||||
pure_virtual ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void* native_handle (char const* type_name) const
|
||||
{
|
||||
char const* const name (typeid (wrapped_type).name ());
|
||||
if (strcmp (name, type_name) == 0)
|
||||
return const_cast <void*> (static_cast <void const*> (&m_object));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
error_code cancel (error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return cancel (ec,
|
||||
EnableIf <has_cancel <wrapped_type,
|
||||
error_code (error_code&)>::value> ());
|
||||
}
|
||||
|
||||
error_code cancel (error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.cancel (ec);
|
||||
}
|
||||
|
||||
error_code cancel (error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
error_code shutdown (shutdown_type what, error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return shutdown (what, ec,
|
||||
EnableIf <has_shutdown <wrapped_type,
|
||||
error_code (shutdown_type, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
|
||||
error_code shutdown (shutdown_type what, error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.shutdown (what, ec);
|
||||
}
|
||||
|
||||
error_code shutdown (shutdown_type, error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
error_code close (error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return close (ec,
|
||||
EnableIf <has_close <wrapped_type,
|
||||
error_code (error_code&)>::value> ());
|
||||
}
|
||||
|
||||
error_code close (error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.close (ec);
|
||||
}
|
||||
|
||||
error_code close (error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// basic_socket_acceptor
|
||||
//
|
||||
|
||||
error_code accept (Socket& peer, error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
typedef typename native_socket <wrapped_type>::socket_type socket_type;
|
||||
return accept (peer, ec,
|
||||
EnableIf <has_accept <wrapped_type,
|
||||
error_code (socket_type&, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
error_code accept (Socket& peer, error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return m_object.accept (
|
||||
native_socket <wrapped_type> (peer).get (), ec);
|
||||
}
|
||||
|
||||
error_code accept (Socket&, error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
async_accept (Socket& peer, BOOST_ASIO_MOVE_ARG(ErrorCall) handler)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
typedef typename native_socket <wrapped_type>::socket_type socket_type;
|
||||
return async_accept (peer, BOOST_ASIO_MOVE_CAST(ErrorCall)(handler),
|
||||
EnableIf <has_async_accept <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
(socket_type&, BOOST_ASIO_MOVE_ARG(TransferCall))>::value> ());
|
||||
}
|
||||
|
||||
template <typename AcceptHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ErrorCall, void (error_code))
|
||||
async_accept (Socket& peer, BOOST_ASIO_MOVE_ARG(AcceptHandler) handler,
|
||||
boost::true_type)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return m_object.async_accept (
|
||||
native_socket <wrapped_type> (peer).get (),
|
||||
BOOST_ASIO_MOVE_CAST(AcceptHandler)(handler));
|
||||
}
|
||||
|
||||
template <typename AcceptHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void (error_code))
|
||||
async_accept (Socket&, BOOST_ASIO_MOVE_ARG(AcceptHandler) handler,
|
||||
boost::false_type)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
AcceptHandler, void (error_code)> init(
|
||||
BOOST_ASIO_MOVE_CAST(AcceptHandler)(handler));
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(AcceptHandler)(handler), ec));
|
||||
return init.result.get();
|
||||
#else
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(AcceptHandler)(handler), ec));
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// basic_stream_socket
|
||||
//
|
||||
|
||||
std::size_t read_some (MutableBuffers const& buffers, error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return read_some (buffers, ec,
|
||||
EnableIf <has_read_some <wrapped_type,
|
||||
std::size_t (MutableBuffers const&, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t read_some (MutableBufferSequence const& buffers, error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.read_some (buffers, ec);
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t read_some (MutableBufferSequence const&, error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
pure_virtual (ec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
std::size_t write_some (ConstBuffers const& buffers, error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return write_some (buffers, ec,
|
||||
EnableIf <has_write_some <wrapped_type,
|
||||
std::size_t (ConstBuffers const&, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
template <typename ConstBufferSequence>
|
||||
std::size_t write_some (ConstBufferSequence const& buffers, error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.write_some (buffers, ec);
|
||||
}
|
||||
|
||||
template <typename ConstBufferSequence>
|
||||
std::size_t write_some (ConstBufferSequence const&, error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
pure_virtual (ec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_read_some (MutableBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return async_read_some (buffers, BOOST_ASIO_MOVE_CAST(TransferCall)(handler),
|
||||
EnableIf <has_async_read_some <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
(MutableBuffers const&, BOOST_ASIO_MOVE_ARG(TransferCall))>::value> ());
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence, typename ReadHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void (error_code, std::size_t))
|
||||
async_read_some (MutableBufferSequence const& buffers, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.async_read_some (buffers,
|
||||
BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||
}
|
||||
|
||||
template <typename MutableBufferSequence, typename ReadHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void (error_code, std::size_t))
|
||||
async_read_some (MutableBufferSequence const&, BOOST_ASIO_MOVE_ARG(ReadHandler) handler,
|
||||
boost::false_type)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
ReadHandler, void (error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (BOOST_ASIO_MOVE_CAST(ReadHandler)(handler), ec, 0));
|
||||
return init.result.get();
|
||||
#else
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (BOOST_ASIO_MOVE_CAST(ReadHandler)(handler), ec, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_write_some (ConstBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return async_write_some (buffers, BOOST_ASIO_MOVE_CAST(TransferCall)(handler),
|
||||
EnableIf <has_async_write_some <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
(ConstBuffers const&, BOOST_ASIO_MOVE_ARG(TransferCall))>::value> ());
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_write_some (ConstBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.async_write_some (buffers,
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_write_some (ConstBuffers const&, BOOST_ASIO_MOVE_ARG(TransferCall) handler,
|
||||
boost::false_type)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
TransferCall, void (error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
return init.result.get();
|
||||
#else
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// ssl::stream
|
||||
//
|
||||
|
||||
bool requires_handshake ()
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return
|
||||
has_handshake <wrapped_type,
|
||||
error_code (handshake_type, error_code&)>::value ||
|
||||
has_async_handshake <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
(handshake_type, BOOST_ASIO_MOVE_ARG(ErrorCall))>::value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
error_code handshake (handshake_type type, error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return handshake (type, ec,
|
||||
EnableIf <has_handshake <wrapped_type,
|
||||
error_code (handshake_type, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
error_code handshake (handshake_type type, error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.handshake (type, ec);
|
||||
}
|
||||
|
||||
error_code handshake (handshake_type, error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
async_handshake (handshake_type type, BOOST_ASIO_MOVE_ARG(ErrorCall) handler)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return async_handshake (type, BOOST_ASIO_MOVE_CAST(ErrorCall)(handler),
|
||||
EnableIf <has_async_handshake <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
(handshake_type, BOOST_ASIO_MOVE_ARG(ErrorCall))>::value> ());
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
async_handshake (handshake_type type, BOOST_ASIO_MOVE_ARG(ErrorCall) handler,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.async_handshake (type,
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
async_handshake (handshake_type, BOOST_ASIO_MOVE_ARG(ErrorCall) handler,
|
||||
boost::false_type)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
ErrorCall, void (error_code)> init(
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
return init.result.get();
|
||||
#else
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#if BEAST_ASIO_HAS_BUFFEREDHANDSHAKE
|
||||
|
||||
error_code handshake (handshake_type type, ConstBuffers const& buffers, error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return handshake (type, buffers, ec,
|
||||
EnableIf <has_handshake <wrapped_type,
|
||||
error_code (handshake_type, ConstBuffers const&, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
error_code handshake (handshake_type type, ConstBuffers const& buffers, error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.handshake (type, buffers, ec);
|
||||
}
|
||||
|
||||
error_code handshake (handshake_type, ConstBuffers const&, error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_handshake (handshake_type type, ConstBuffers const& buffers,
|
||||
BOOST_ASIO_MOVE_ARG(TransferCall) handler)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return async_handshake (type, buffers, BOOST_ASIO_MOVE_CAST(TransferCall)(handler),
|
||||
EnableIf <has_async_handshake <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
(handshake_type, ConstBuffers const&, error_code&)>::value> ());
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_handshake (handshake_type type, ConstBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.async_handshake (type, buffers,
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (error_code, std::size_t))
|
||||
async_handshake (handshake_type, ConstBuffers const&, BOOST_ASIO_MOVE_ARG(TransferCall) handler,
|
||||
boost::false_type)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
TransferCall, void (error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler));
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
return init.result.get();
|
||||
#else
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(TransferCall)(handler), ec, 0));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
error_code shutdown (error_code& ec)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return shutdown (ec,
|
||||
EnableIf <has_shutdown <wrapped_type,
|
||||
error_code (error_code&)>::value> ());
|
||||
}
|
||||
|
||||
error_code shutdown (error_code& ec,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.shutdown (ec);
|
||||
}
|
||||
|
||||
error_code shutdown (error_code& ec,
|
||||
boost::false_type)
|
||||
{
|
||||
return pure_virtual (ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void async_shutdown (BOOST_ASIO_MOVE_ARG(ErrorCall) handler)
|
||||
{
|
||||
using namespace SocketWrapperMemberChecks;
|
||||
return async_shutdown (BOOST_ASIO_MOVE_CAST(ErrorCall)(handler),
|
||||
EnableIf <has_async_shutdown <wrapped_type,
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
(BOOST_ASIO_MOVE_ARG(ErrorCall))>::value> ());
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
async_shutdown (BOOST_ASIO_MOVE_ARG(ErrorCall) handler,
|
||||
boost::true_type)
|
||||
{
|
||||
return m_object.async_shutdown (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
}
|
||||
|
||||
BEAST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (error_code))
|
||||
async_shutdown (BOOST_ASIO_MOVE_ARG(ErrorCall) handler,
|
||||
boost::false_type)
|
||||
{
|
||||
#if BEAST_ASIO_HAS_FUTURE_RETURNS
|
||||
boost::asio::detail::async_result_init<
|
||||
ErrorCall, void (error_code, std::size_t)> init(
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler));
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
return init.result.get();
|
||||
#else
|
||||
error_code ec;
|
||||
ec = pure_virtual (ec);
|
||||
get_io_service ().post (boost::bind (
|
||||
BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec));
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
WrappedObject m_object;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,136 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SOCKETWRAPPERBASICS_H_INCLUDED
|
||||
#define BEAST_SOCKETWRAPPERBASICS_H_INCLUDED
|
||||
|
||||
/** Some utilities for SocketWrapper and others.
|
||||
*/
|
||||
class SocketWrapperBasics
|
||||
{
|
||||
public:
|
||||
#if 0
|
||||
/** Template specialization to determine available interfaces. */
|
||||
template <typename Object>
|
||||
struct InterfacesOf
|
||||
{
|
||||
/** Intrusive tag support.
|
||||
|
||||
To use this, add a struct called SocketInterfaces to your
|
||||
class and derive it from the interfaces that you support.
|
||||
For example:
|
||||
|
||||
@code
|
||||
|
||||
struct MyHandshakingStream
|
||||
{
|
||||
struct SocketInterfaces
|
||||
: SocketInterface::Stream
|
||||
, SocketInterface::Handshake
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
@endcode
|
||||
*/
|
||||
typedef typename Object::SocketInterfaces type;
|
||||
typedef type value;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::basic_socket_acceptor
|
||||
template <typename Protocol, typename SocketService>
|
||||
struct InterfacesOf <boost::asio::basic_socket_acceptor <Protocol, SocketService> >
|
||||
{
|
||||
struct value : SocketInterface::Acceptor { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::basic_socket
|
||||
template <typename Protocol, typename SocketService>
|
||||
struct InterfacesOf <boost::asio::basic_socket <Protocol, SocketService> >
|
||||
{
|
||||
struct value : SocketInterface::Socket { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::basic_stream_socket
|
||||
template <typename Protocol, typename SocketService>
|
||||
struct InterfacesOf <boost::asio::basic_stream_socket <Protocol, SocketService> >
|
||||
{
|
||||
struct value : SocketInterface::Socket, SocketInterface::Stream { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::buffered_stream
|
||||
template <typename Stream>
|
||||
struct InterfacesOf <boost::asio::buffered_stream <Stream> >
|
||||
{
|
||||
struct value : SocketInterface::Stream { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::buffered_read_stream
|
||||
template <typename Stream>
|
||||
struct InterfacesOf <boost::asio::buffered_read_stream <Stream> >
|
||||
{
|
||||
struct value : SocketInterface::Stream { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::buffered_write_stream
|
||||
template <typename Stream>
|
||||
struct InterfacesOf <boost::asio::buffered_write_stream <Stream> >
|
||||
{
|
||||
struct value : SocketInterface::Stream { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
// Specialization for boost::asio::ssl::stream
|
||||
template <typename Stream>
|
||||
struct InterfacesOf <boost::asio::ssl::stream <Stream> >
|
||||
{
|
||||
struct value : SocketInterface::Stream , SocketInterface::Handshake { };
|
||||
typedef value type;
|
||||
};
|
||||
|
||||
#if 1
|
||||
// Less elegant, but works.
|
||||
// Determines if Object supports the specified Interface
|
||||
template <typename Object, typename Interface, class Enable = void>
|
||||
struct HasInterface : boost::false_type { };
|
||||
|
||||
template <typename Object, typename Interface>
|
||||
struct HasInterface <Object, Interface,
|
||||
typename boost::enable_if <boost::is_base_of <
|
||||
Interface, typename InterfacesOf <Object>::type> >::type >
|
||||
: boost::true_type { };
|
||||
#else
|
||||
// This should work, but doesn't.
|
||||
// K-ballo from #boost suggested it.
|
||||
//
|
||||
// Determines if Object supports the specified Interface
|
||||
template <typename Object, typename Interface>
|
||||
struct HasInterface : boost::is_base_of <Interface, typename InterfacesOf <Object> >
|
||||
{
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
/** Add this to get the @ref beast_basics module.
|
||||
|
||||
@file beast_basics.cpp
|
||||
@ingroup beast_basics
|
||||
*/
|
||||
|
||||
#include "BeastConfig.h"
|
||||
|
||||
#include "beast_basics.h"
|
||||
|
||||
#if BEAST_MSVC && _DEBUG
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4100) // unreferenced formal parmaeter
|
||||
#pragma warning (disable: 4355) // 'this' used in base member
|
||||
#endif
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
#include "events/beast_DeadlineTimer.cpp"
|
||||
#include "events/beast_OncePerSecond.cpp"
|
||||
|
||||
#include "threads/beast_InterruptibleThread.cpp"
|
||||
#include "threads/beast_Semaphore.cpp"
|
||||
#include "memory/beast_FifoFreeStoreWithTLS.cpp"
|
||||
#include "memory/beast_FifoFreeStoreWithoutTLS.cpp"
|
||||
#include "memory/beast_GlobalPagedFreeStore.cpp"
|
||||
#include "memory/beast_PagedFreeStore.cpp"
|
||||
#include "threads/beast_CallQueue.cpp"
|
||||
#include "threads/beast_Listeners.cpp"
|
||||
#include "threads/beast_ManualCallQueue.cpp"
|
||||
#include "threads/beast_ParallelFor.cpp"
|
||||
#include "threads/beast_ReadWriteMutex.cpp"
|
||||
#include "threads/beast_ThreadGroup.cpp"
|
||||
#include "threads/beast_ThreadWithCallQueue.cpp"
|
||||
#include "threads/beast_Workers.cpp"
|
||||
|
||||
}
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
@@ -1,276 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
/** Include this to get the @ref beast_basics module.
|
||||
|
||||
@file beast_basics.h
|
||||
@ingroup beast_basics
|
||||
*/
|
||||
|
||||
#ifndef BEAST_BASICS_H_INCLUDED
|
||||
#define BEAST_BASICS_H_INCLUDED
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* If you fail to make sure that all your compile units are building Beast with
|
||||
the same set of option flags, then there's a risk that different compile
|
||||
units will treat the classes as having different memory layouts, leading to
|
||||
very nasty memory corruption errors when they all get linked together.
|
||||
That's why it's best to always include the BeastConfig.h file before any
|
||||
beast headers.
|
||||
*/
|
||||
#ifndef BEAST_BEASTCONFIG_H_INCLUDED
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ("Have you included your BeastConfig.h file before including the Beast headers?")
|
||||
# else
|
||||
# warning "Have you included your BeastConfig.h file before including the Beast headers?"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
|
||||
@mainpage Beast: A C++ library for server development.
|
||||
|
||||
### Version 1.0
|
||||
|
||||
Copyright 2008, 2013 by Vinnie Falco \<vinnie.falco@gmail.com\> ([e-mail][0])
|
||||
|
||||
Beast is a source code collection of individual modules containing
|
||||
functionality for a variety of applications, with an emphasis on building
|
||||
concurrent systems. Beast incorporates parts of [JUCE][3] (Jules' Utility
|
||||
Class Extensions), available from [Raw Material Software][4]. Beast has no
|
||||
external dependencies
|
||||
|
||||
Beast is hosted on Github at [https://github.com/vinniefalco/Beast][1]
|
||||
|
||||
The online documentation is at [http://vinniefalco.github.com/Beast][2]
|
||||
|
||||
## Platforms
|
||||
|
||||
All platforms supported by JUCE are also supported by Beast. Currently these
|
||||
platforms include:
|
||||
|
||||
- **Windows**: Applications and VST/RTAS/NPAPI/ActiveX plugins can be built
|
||||
using MS Visual Studio. The results are all fully compatible with Windows
|
||||
XP, Vista or Windows 7.
|
||||
|
||||
- **Mac OS X**: Applications and VST/AudioUnit/RTAS/NPAPI plugins with Xcode.
|
||||
|
||||
- **GNU/Linux**: Applications and plugins can be built for any kernel 2.6 or
|
||||
later.
|
||||
|
||||
- **FreeBSD**: Kernel version 8.4 or higher required.
|
||||
|
||||
- **iOS**: Native iPhone and iPad apps.
|
||||
|
||||
- **Android**: Supported.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This documentation assumes that the reader has a working knowledge of JUCE.
|
||||
Some modules built on external libraries assume that the reader understands
|
||||
the operation of those external libraries. Certain modules assume that the
|
||||
reader understands additional domain-specific information. Modules with
|
||||
additional prerequisites are marked in the documentation.
|
||||
|
||||
## External Modules
|
||||
|
||||
Some modules bring in functionality provided by external libraries. For
|
||||
example, the @ref beast_bzip2 module provides the compression and decompression
|
||||
algorithms in [bZip2][7]. Usage of these external library modules is optional.
|
||||
They come with complete source code, as well as options for using either
|
||||
system or user provided variants of the external libraries: it is not
|
||||
necessary to download additional source code packages to use these modules.
|
||||
|
||||
External code incorporated into Beast is covered by separate licenses. See
|
||||
the licensing information and notes in the corresponding source files for
|
||||
copyright information and terms of use.
|
||||
|
||||
## Integration
|
||||
|
||||
Beast requires recent versions of JUCE. It won't work with versions 1.53 or
|
||||
earlier. To use the library it is necessary to first download JUCE to a
|
||||
location where your development environment can find it. Or, you can use your
|
||||
existing installation of JUCE.
|
||||
|
||||
This library uses the same modularized organizational structure as JUCE. To
|
||||
use a module, first add a path to the list of includes searched by your
|
||||
development environment or project, which points to the Beast directory. Then,
|
||||
add the single corresponding .c or .cpp file to your existing project which
|
||||
already uses JUCE. For example, to use the @ref beast_core module, add the file
|
||||
beast_core.cpp to your project. Some modules depend on other modules.
|
||||
|
||||
To use a module, include the appropriate header from within your source code.
|
||||
For example, to access classes in the @ref beast_concurrent module, use this:
|
||||
|
||||
@code
|
||||
|
||||
#include "modules/beast_concurrent/beast_concurrent.h"
|
||||
|
||||
@endcode
|
||||
|
||||
Then add the corresponding file beast_concurrent.cpp to your build.
|
||||
|
||||
## AppConfig
|
||||
|
||||
Some Beast features can be controlled at compilation time through
|
||||
preprocessor directives. The available choices of compilation options are
|
||||
described in AppConfig.h, located in the AppConfigTemplate directory. Copy
|
||||
the provided settings into your existing AppConfig.h (a file used by JUCE
|
||||
convention).
|
||||
|
||||
## License
|
||||
|
||||
This library contains portions of other open source products covered by
|
||||
separate licenses. Please see the corresponding source files for specific
|
||||
terms.
|
||||
|
||||
Beast is provided under the terms of The ISC License:
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
Some files contain portions of these external projects, licensed separately:
|
||||
|
||||
- [bZip2][7] is Copyright (C) 1996-2010 Julian R Seward. All rights
|
||||
reserved. See the corresponding file LICENSE for licensing terms.
|
||||
|
||||
- [Soci][13] is Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton, and
|
||||
various others noted in the corresponding source files. Soci is distributed
|
||||
under the [Boost Software License, Version 1.0][14].
|
||||
|
||||
- [SQLite][15], placed in the public domain.
|
||||
|
||||
[0]: mailto:vinnie.falco@gmail.com "Vinnie Falco (Email)"
|
||||
[1]: https://github.com/vinniefalco/Beast "Beast Project"
|
||||
[2]: http://vinniefalco.github.com/Beast/ "Beast Documentation"
|
||||
[3]: http://rawmaterialsoftware.com/juce.php "JUCE"
|
||||
[4]: http://rawmaterialsoftware.com/ "Raw Material Software"
|
||||
[5]: http://www.gnu.org/licenses/gpl-2.0.html "GNU General Public License, version 2"
|
||||
[6]: http://rawmaterialsoftware.com/jucelicense.php "JUCE Licenses"
|
||||
[7]: http://www.bzip.org/ "bZip2: Home"
|
||||
[8]: http://freetype.org/ "The FreeType Project"
|
||||
[9]: http://www.freetype.org/FTL.TXT "The FreeType Project License"
|
||||
[10]: http://www.lua.org/ "The Programming Language Lua"
|
||||
[11]: http://opensource.org/licenses/ISC "The ISC License"
|
||||
[12]: https://github.com/vinniefalco/LuaBridge
|
||||
[13]: http://soci.sourceforge.net/ "SOCI"
|
||||
[14]: http://www.boost.org/LICENSE_1_0.txt "Boost Software License, Version 1.0"
|
||||
[15]: http://sqlite.org/ "SQLite Home Page"
|
||||
[16]: http://developer.kde.org/~wheeler/taglib.html "TagLib"
|
||||
[17]: http://www.gnu.org/licenses/lgpl-2.1.html "Gnu Lesser General Public License, version 2.1"
|
||||
[18]: http://www.mozilla.org/MPL/1.1/ "Mozilla Public License"
|
||||
|
||||
@copyright Copyright 2008-2013 by Vinnie Falco \<vinnie.falco@gmail.com\> ([e-mail][0])
|
||||
@copyright Provided under the [ISC LIcense][11]
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Implementation classes.
|
||||
|
||||
Thase classes are used internally.
|
||||
|
||||
@defgroup internal internal
|
||||
@internal
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** External modules.
|
||||
|
||||
These modules bring in functionality from third party or system libraries.
|
||||
|
||||
@defgroup external external
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Core classes.
|
||||
|
||||
This module provides core required functionality, and classes useful for
|
||||
general development. All other modules require this module.
|
||||
|
||||
@todo Discuss the treatment of exceptions versus Error objects in the
|
||||
library.
|
||||
|
||||
@todo Discuss the additions to BeastConfig.h
|
||||
|
||||
@defgroup beast_core beast_core
|
||||
*/
|
||||
|
||||
/* Get this early so we can use it. */
|
||||
#include "../beast_core/system/beast_TargetPlatform.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BEAST_BOOST_IS_AVAILABLE
|
||||
#include <boost/thread/tss.hpp>
|
||||
#endif
|
||||
|
||||
#include "../beast_core/beast_core.h"
|
||||
|
||||
/** The Beast namespace.
|
||||
|
||||
This namespace contains all Beast symbols.
|
||||
*/
|
||||
namespace beast
|
||||
{
|
||||
|
||||
// Order matters
|
||||
|
||||
#include "functor/beast_Function.h"
|
||||
#include "events/beast_DeadlineTimer.h"
|
||||
#include "events/beast_OncePerSecond.h"
|
||||
#include "math/beast_Math.h"
|
||||
#include "memory/beast_AllocatedBy.h"
|
||||
#include "memory/beast_PagedFreeStore.h"
|
||||
#include "memory/beast_GlobalPagedFreeStore.h"
|
||||
#include "memory/beast_FifoFreeStoreWithTLS.h"
|
||||
#include "memory/beast_FifoFreeStoreWithoutTLS.h"
|
||||
#include "memory/beast_FifoFreeStore.h"
|
||||
#include "memory/beast_GlobalFifoFreeStore.h"
|
||||
#include "threads/beast_Semaphore.h"
|
||||
#include "threads/beast_SerialFor.h"
|
||||
#include "threads/beast_InterruptibleThread.h"
|
||||
#include "threads/beast_ReadWriteMutex.h"
|
||||
#include "threads/beast_ThreadGroup.h"
|
||||
#include "threads/beast_CallQueue.h"
|
||||
#include "threads/beast_SharedData.h"
|
||||
#include "threads/beast_GlobalThreadGroup.h"
|
||||
#include "threads/beast_Listeners.h"
|
||||
#include "threads/beast_ManualCallQueue.h"
|
||||
#include "threads/beast_ParallelFor.h"
|
||||
#include "threads/beast_ThreadWithCallQueue.h"
|
||||
#include "threads/beast_Workers.h"
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class DeadlineTimer::Manager
|
||||
: public SharedSingleton <DeadlineTimer::Manager>
|
||||
, public InterruptibleThread::EntryPoint
|
||||
{
|
||||
private:
|
||||
typedef CriticalSection LockType;
|
||||
typedef List <DeadlineTimer> Items;
|
||||
|
||||
public:
|
||||
Manager ()
|
||||
: SharedSingleton <Manager> (SingletonLifetime::persistAfterCreation)
|
||||
, m_shouldStop (false)
|
||||
, m_thread ("DeadlineTimer::Manager")
|
||||
{
|
||||
m_thread.start (this);
|
||||
}
|
||||
|
||||
~Manager ()
|
||||
{
|
||||
m_shouldStop = true;
|
||||
|
||||
m_thread.interrupt ();
|
||||
|
||||
bassert (m_items.empty ());
|
||||
}
|
||||
|
||||
// Okay to call on an active timer.
|
||||
// However, an extra notification may still happen due to concurrency.
|
||||
//
|
||||
void activate (DeadlineTimer* timer, double secondsRecurring, Time const& when)
|
||||
{
|
||||
bassert (secondsRecurring >= 0);
|
||||
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
if (timer->m_isActive)
|
||||
{
|
||||
m_items.erase (m_items.iterator_to (*timer));
|
||||
|
||||
timer->m_isActive = false;
|
||||
}
|
||||
|
||||
timer->m_secondsRecurring = secondsRecurring;
|
||||
timer->m_notificationTime = when;
|
||||
|
||||
insertSorted (*timer);
|
||||
timer->m_isActive = true;
|
||||
|
||||
m_thread.interrupt ();
|
||||
}
|
||||
|
||||
// Okay to call this on an inactive timer.
|
||||
// This can happen naturally based on concurrency.
|
||||
//
|
||||
void deactivate (DeadlineTimer* timer)
|
||||
{
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
if (timer->m_isActive)
|
||||
{
|
||||
m_items.erase (m_items.iterator_to (*timer));
|
||||
|
||||
timer->m_isActive = false;
|
||||
}
|
||||
|
||||
m_thread.interrupt ();
|
||||
}
|
||||
|
||||
void threadRun ()
|
||||
{
|
||||
while (! m_shouldStop)
|
||||
{
|
||||
Time const currentTime = Time::getCurrentTime ();
|
||||
double seconds = 0;
|
||||
|
||||
{
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
// Notify everyone whose timer has expired
|
||||
//
|
||||
if (! m_items.empty ())
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
Items::iterator const iter = m_items.begin ();
|
||||
|
||||
// Has this timer expired?
|
||||
if (iter->m_notificationTime <= currentTime)
|
||||
{
|
||||
// Yes, so call the listener.
|
||||
//
|
||||
// Note that this happens while the lock is held.
|
||||
//
|
||||
iter->m_listener->onDeadlineTimer (*iter);
|
||||
|
||||
// Remove it from the list.
|
||||
m_items.erase (iter);
|
||||
|
||||
// Is the timer recurring?
|
||||
if (iter->m_secondsRecurring > 0)
|
||||
{
|
||||
// Yes so set the timer again.
|
||||
iter->m_notificationTime =
|
||||
currentTime + RelativeTime (iter->m_secondsRecurring);
|
||||
|
||||
// Keep it active.
|
||||
insertSorted (*iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a recurring timer, deactivate it.
|
||||
iter->m_isActive = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out how long we need to wait.
|
||||
// This has to be done while holding the lock.
|
||||
//
|
||||
if (! m_items.empty ())
|
||||
{
|
||||
seconds = (m_items.front ().m_notificationTime - currentTime).inSeconds ();
|
||||
}
|
||||
else
|
||||
{
|
||||
seconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we have released the lock here.
|
||||
//
|
||||
if (seconds > 0)
|
||||
{
|
||||
// Wait until interrupt or next timer.
|
||||
//
|
||||
m_thread.wait (static_cast <int> (seconds * 1000 + 0.5));
|
||||
}
|
||||
else if (seconds == 0)
|
||||
{
|
||||
// Wait until interrupt
|
||||
//
|
||||
m_thread.wait ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not wait. This can happen if the recurring timer duration
|
||||
// is extremely short, or if a listener wastes too much time in
|
||||
// their callback.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Caller is responsible for locking
|
||||
void insertSorted (DeadlineTimer& item)
|
||||
{
|
||||
if (! m_items.empty ())
|
||||
{
|
||||
Items::iterator before = m_items.begin ();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (before->m_notificationTime >= item.m_notificationTime)
|
||||
{
|
||||
m_items.insert (before, item);
|
||||
break;
|
||||
}
|
||||
|
||||
++before;
|
||||
|
||||
if (before == m_items.end ())
|
||||
{
|
||||
m_items.push_back (item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.push_back (item);
|
||||
}
|
||||
}
|
||||
|
||||
static Manager* createInstance ()
|
||||
{
|
||||
return new Manager;
|
||||
}
|
||||
|
||||
private:
|
||||
CriticalSection m_mutex;
|
||||
bool volatile m_shouldStop;
|
||||
InterruptibleThread m_thread;
|
||||
Items m_items;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DeadlineTimer::DeadlineTimer (Listener* listener)
|
||||
: m_listener (listener)
|
||||
, m_manager (Manager::getInstance ())
|
||||
, m_isActive (false)
|
||||
{
|
||||
}
|
||||
|
||||
DeadlineTimer::~DeadlineTimer ()
|
||||
{
|
||||
m_manager->deactivate (this);
|
||||
}
|
||||
|
||||
void DeadlineTimer::setExpiration (double secondsUntilDeadline)
|
||||
{
|
||||
bassert (secondsUntilDeadline > 0);
|
||||
|
||||
Time const when = Time::getCurrentTime () + RelativeTime (secondsUntilDeadline);
|
||||
|
||||
m_manager->activate (this, 0, when);
|
||||
}
|
||||
|
||||
void DeadlineTimer::setRecurringExpiration (double secondsUntilDeadline)
|
||||
{
|
||||
bassert (secondsUntilDeadline > 0);
|
||||
|
||||
Time const when = Time::getCurrentTime () + RelativeTime (secondsUntilDeadline);
|
||||
|
||||
m_manager->activate (this, secondsUntilDeadline, when);
|
||||
}
|
||||
|
||||
void DeadlineTimer::setExpirationTime (Time const& when)
|
||||
{
|
||||
m_manager->activate (this, 0, when);
|
||||
}
|
||||
|
||||
void DeadlineTimer::reset ()
|
||||
{
|
||||
m_manager->deactivate (this);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class OncePerSecond::TimerSingleton
|
||||
: public SharedSingleton <OncePerSecond::TimerSingleton>
|
||||
, private InterruptibleThread::EntryPoint
|
||||
{
|
||||
private:
|
||||
TimerSingleton ()
|
||||
: SharedSingleton <OncePerSecond::TimerSingleton> (
|
||||
SingletonLifetime::persistAfterCreation)
|
||||
, m_thread ("Once Per Second")
|
||||
{
|
||||
m_thread.start (this);
|
||||
}
|
||||
|
||||
~TimerSingleton ()
|
||||
{
|
||||
m_thread.join ();
|
||||
|
||||
bassert (m_list.empty ());
|
||||
}
|
||||
|
||||
void threadRun ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
bool const interrupted = m_thread.wait (1000);
|
||||
|
||||
if (interrupted)
|
||||
break;
|
||||
|
||||
notify ();
|
||||
}
|
||||
}
|
||||
|
||||
void notify ()
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
|
||||
for (List <Elem>::iterator iter = m_list.begin (); iter != m_list.end ();)
|
||||
{
|
||||
OncePerSecond* object = iter->object;
|
||||
++iter;
|
||||
object->doOncePerSecond ();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void insert (Elem* elem)
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
|
||||
m_list.push_back (*elem);
|
||||
}
|
||||
|
||||
void remove (Elem* elem)
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
|
||||
m_list.erase (m_list.iterator_to (*elem));
|
||||
}
|
||||
|
||||
static TimerSingleton* createInstance ()
|
||||
{
|
||||
return new TimerSingleton;
|
||||
}
|
||||
|
||||
private:
|
||||
InterruptibleThread m_thread;
|
||||
CriticalSection m_mutex;
|
||||
List <Elem> m_list;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
OncePerSecond::OncePerSecond ()
|
||||
{
|
||||
m_elem.instance = TimerSingleton::getInstance ();
|
||||
m_elem.object = this;
|
||||
}
|
||||
|
||||
OncePerSecond::~OncePerSecond ()
|
||||
{
|
||||
}
|
||||
|
||||
void OncePerSecond::startOncePerSecond ()
|
||||
{
|
||||
m_elem.instance->insert (&m_elem);
|
||||
}
|
||||
|
||||
void OncePerSecond::endOncePerSecond ()
|
||||
{
|
||||
m_elem.instance->remove (&m_elem);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ONCEPERSECOND_BEASTHEADER
|
||||
#define BEAST_ONCEPERSECOND_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Provides a once per second notification.
|
||||
|
||||
Derive your class from OncePerSecond and override doOncePerSecond(). Then,
|
||||
call startOncePerSecond() to begin receiving the notifications. No clean-up
|
||||
or other actions are required.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API OncePerSecond : public Uncopyable
|
||||
{
|
||||
public:
|
||||
OncePerSecond ();
|
||||
virtual ~OncePerSecond ();
|
||||
|
||||
/** Begin receiving notifications. */
|
||||
void startOncePerSecond ();
|
||||
|
||||
/** Stop receiving notifications. */
|
||||
void endOncePerSecond ();
|
||||
|
||||
protected:
|
||||
/** Called once per second. */
|
||||
virtual void doOncePerSecond () = 0;
|
||||
|
||||
private:
|
||||
class TimerSingleton;
|
||||
typedef SharedObjectPtr <TimerSingleton> TimerPtr;
|
||||
|
||||
struct Elem : List <Elem>::Node
|
||||
{
|
||||
TimerPtr instance;
|
||||
OncePerSecond* object;
|
||||
};
|
||||
|
||||
Elem m_elem;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,266 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_FUNCTION_BEASTHEADER
|
||||
#define BEAST_FUNCTION_BEASTHEADER
|
||||
|
||||
//
|
||||
// Strong replacement for boost::function:
|
||||
//
|
||||
// #1 Bounded memory requirement, avoids the free store.
|
||||
//
|
||||
// #2 Always refers to a functor (i.e. is never invalid)
|
||||
//
|
||||
// #3 Default value (None) is a function that
|
||||
// returns a default object (the result type
|
||||
// constructed with a default constructor).
|
||||
//
|
||||
|
||||
template <typename Signature, int Bytes = 128>
|
||||
class Function;
|
||||
|
||||
//
|
||||
// nullary function
|
||||
//
|
||||
|
||||
template <typename R, int Bytes>
|
||||
class Function <R (void), Bytes>
|
||||
{
|
||||
public:
|
||||
typedef R result_type;
|
||||
typedef Function self_type;
|
||||
|
||||
struct None
|
||||
{
|
||||
typedef R result_type;
|
||||
result_type operator () () const
|
||||
{
|
||||
return result_type ();
|
||||
}
|
||||
};
|
||||
|
||||
Function ()
|
||||
{
|
||||
constructCopyOf (None ());
|
||||
}
|
||||
|
||||
Function (Function const& f)
|
||||
{
|
||||
f.getCall ().constructCopyInto (m_storage);
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
Function (Functor const& f)
|
||||
{
|
||||
constructCopyOf (f);
|
||||
}
|
||||
|
||||
~Function ()
|
||||
{
|
||||
getCall ().~Call ();
|
||||
}
|
||||
|
||||
Function& operator= (Function const& f)
|
||||
{
|
||||
getCall ().~Call ();
|
||||
f.getCall ().constructCopyInto (m_storage);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
Function& operator= (Functor const& f)
|
||||
{
|
||||
getCall ().~Call ();
|
||||
constructCopyOf (f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
result_type operator () ()
|
||||
{
|
||||
return getCall ().operator () ();
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Functor>
|
||||
void constructCopyOf (Functor const& f)
|
||||
{
|
||||
// If this generates a compile error it means that
|
||||
// the functor is too large for the static buffer.
|
||||
// Increase the storage template parameter until
|
||||
// the error message goes away. This might cause
|
||||
// changes throughout the application with other
|
||||
// template classes that depend on the size.
|
||||
static_bassert (sizeof (StoredCall <Functor>) <= Bytes);
|
||||
new (m_storage) StoredCall <Functor> (f);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Call
|
||||
{
|
||||
virtual ~Call () {}
|
||||
virtual void constructCopyInto (void* p) const = 0;
|
||||
virtual result_type operator () () = 0;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
struct StoredCall : Call
|
||||
{
|
||||
explicit StoredCall (Functor const& f) : m_f (f) { }
|
||||
StoredCall (const StoredCall& c) : m_f (c.m_f) { }
|
||||
void constructCopyInto (void* p) const
|
||||
{
|
||||
new (p) StoredCall (m_f);
|
||||
}
|
||||
result_type operator () ()
|
||||
{
|
||||
return m_f ();
|
||||
}
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
Call& getCall ()
|
||||
{
|
||||
return *reinterpret_cast <Call*> (&m_storage[0]);
|
||||
}
|
||||
|
||||
Call const& getCall () const
|
||||
{
|
||||
return *reinterpret_cast <Call const*> (&m_storage[0]);
|
||||
}
|
||||
|
||||
char m_storage [Bytes]; // should be enough
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// unary function
|
||||
//
|
||||
|
||||
template <typename R, typename T1, int Bytes>
|
||||
class Function <R (T1 t1), Bytes>
|
||||
{
|
||||
public:
|
||||
typedef R result_type;
|
||||
typedef Function self_type;
|
||||
|
||||
struct None
|
||||
{
|
||||
typedef R result_type;
|
||||
result_type operator () (T1) const
|
||||
{
|
||||
return result_type ();
|
||||
}
|
||||
};
|
||||
|
||||
Function ()
|
||||
{
|
||||
constructCopyOf (None ());
|
||||
}
|
||||
|
||||
Function (const Function& f)
|
||||
{
|
||||
f.getCall ().constructCopyInto (m_storage);
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
Function (Functor const& f)
|
||||
{
|
||||
constructCopyOf (f);
|
||||
}
|
||||
|
||||
~Function ()
|
||||
{
|
||||
getCall ().~Call ();
|
||||
}
|
||||
|
||||
Function& operator= (const Function& f)
|
||||
{
|
||||
getCall ().~Call ();
|
||||
f.getCall ().constructCopyInto (m_storage);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
Function& operator= (Functor const& f)
|
||||
{
|
||||
getCall ().~Call ();
|
||||
constructCopyOf (f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
result_type operator () (T1 t1)
|
||||
{
|
||||
return getCall ().operator () (t1);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Functor>
|
||||
void constructCopyOf (Functor const& f)
|
||||
{
|
||||
// If this generates a compile error it means that
|
||||
// the functor is too large for the static buffer.
|
||||
// Increase the storage template parameter until
|
||||
// the error message goes away. This might cause
|
||||
// changes throughout the application with other
|
||||
// template classes that depend on the size.
|
||||
static_bassert (sizeof (StoredCall <Functor>) <= Bytes);
|
||||
new (m_storage) StoredCall <Functor> (f);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Call
|
||||
{
|
||||
virtual ~Call () {}
|
||||
virtual void constructCopyInto (void* p) const = 0;
|
||||
virtual result_type operator () (T1 t1) = 0;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
struct StoredCall : Call
|
||||
{
|
||||
explicit StoredCall (Functor const& f) : m_f (f) { }
|
||||
StoredCall (const StoredCall& c) : m_f (c.m_f) { }
|
||||
void constructCopyInto (void* p) const
|
||||
{
|
||||
new (p) StoredCall (m_f);
|
||||
}
|
||||
result_type operator () (T1 t1)
|
||||
{
|
||||
return m_f (t1);
|
||||
}
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
Call& getCall ()
|
||||
{
|
||||
return *reinterpret_cast <Call*> (&m_storage[0]);
|
||||
}
|
||||
|
||||
Call const& getCall () const
|
||||
{
|
||||
return *reinterpret_cast <Call const*> (&m_storage[0]);
|
||||
}
|
||||
|
||||
char m_storage [Bytes]; // should be enough
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ALLOCATEDBY_BEASTHEADER
|
||||
#define BEAST_ALLOCATEDBY_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Customized allocation for heap objects.
|
||||
|
||||
Derived classes will use the specified allocator for new and delete.
|
||||
|
||||
@param AllocatorType The type of allocator to use.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class AllocatorType>
|
||||
class AllocatedBy
|
||||
{
|
||||
public:
|
||||
static inline void* operator new (size_t bytes, AllocatorType& allocator) noexcept
|
||||
{
|
||||
return allocator.allocate (bytes);
|
||||
}
|
||||
|
||||
static inline void* operator new (size_t bytes, AllocatorType* allocator) noexcept
|
||||
{
|
||||
return allocator->allocate (bytes);
|
||||
}
|
||||
|
||||
static inline void operator delete (void* p, AllocatorType&) noexcept
|
||||
{
|
||||
AllocatorType::deallocate (p);
|
||||
}
|
||||
|
||||
static inline void operator delete (void* p, AllocatorType*) noexcept
|
||||
{
|
||||
AllocatorType::deallocate (p);
|
||||
}
|
||||
|
||||
static inline void operator delete (void* p) noexcept
|
||||
{
|
||||
AllocatorType::deallocate (p);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,198 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// Implementation notes
|
||||
//
|
||||
// - A Page is a large allocation from a global PageAllocator.
|
||||
//
|
||||
// - Each thread maintains an 'active' page from which it makes allocations.
|
||||
//
|
||||
// - When the active page is full, a new one takes it's place.
|
||||
//
|
||||
// - Page memory is deallocated when it is not active and no longer referenced.
|
||||
//
|
||||
// - Each instance of FifoFreeStoreWithTLS maintains its own set of per-thread active pages,
|
||||
// but uses a global PageAllocator. This reduces memory consumption without
|
||||
// affecting performance.
|
||||
//
|
||||
|
||||
#if BEAST_BOOST_IS_AVAILABLE
|
||||
|
||||
// This precedes every allocation
|
||||
//
|
||||
struct FifoFreeStoreWithTLS::Header
|
||||
{
|
||||
FifoFreeStoreWithTLS::Page* page;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class FifoFreeStoreWithTLS::Page : LeakChecked <Page>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit Page (const size_t bytes) : m_refs (1)
|
||||
{
|
||||
m_end = reinterpret_cast <char*> (this) + bytes;
|
||||
m_free = reinterpret_cast <char*> (
|
||||
Memory::pointerAdjustedForAlignment (this + 1));
|
||||
}
|
||||
|
||||
~Page ()
|
||||
{
|
||||
bassert (! m_refs.isSignaled ());
|
||||
}
|
||||
|
||||
inline bool release ()
|
||||
{
|
||||
bassert (! m_refs.isSignaled ());
|
||||
|
||||
return m_refs.release ();
|
||||
}
|
||||
|
||||
void* allocate (size_t bytes)
|
||||
{
|
||||
bassert (bytes > 0);
|
||||
|
||||
char* p = Memory::pointerAdjustedForAlignment (m_free);
|
||||
char* free = p + bytes;
|
||||
|
||||
if (free <= m_end)
|
||||
{
|
||||
m_free = free;
|
||||
|
||||
m_refs.addref ();
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 0;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
AtomicCounter m_refs; // reference count
|
||||
char* m_free; // next free byte
|
||||
char* m_end; // last free byte + 1
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class FifoFreeStoreWithTLS::PerThreadData : LeakChecked <PerThreadData>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit PerThreadData (FifoFreeStoreWithTLS* allocator)
|
||||
: m_allocator (*allocator)
|
||||
, m_active (m_allocator.newPage ())
|
||||
{
|
||||
}
|
||||
|
||||
~PerThreadData ()
|
||||
{
|
||||
if (m_active->release ())
|
||||
m_allocator.deletePage (m_active);
|
||||
}
|
||||
|
||||
inline void* allocate (const size_t bytes)
|
||||
{
|
||||
const size_t headerBytes = Memory::sizeAdjustedForAlignment (sizeof (Header));
|
||||
const size_t bytesNeeded = headerBytes + bytes;
|
||||
|
||||
if (bytesNeeded > m_allocator.m_pages->getPageBytes ())
|
||||
Throw (Error ().fail (__FILE__, __LINE__, TRANS ("the memory request was too large")));
|
||||
|
||||
Header* header;
|
||||
|
||||
header = reinterpret_cast <Header*> (m_active->allocate (bytesNeeded));
|
||||
|
||||
if (!header)
|
||||
{
|
||||
if (m_active->release ())
|
||||
deletePage (m_active);
|
||||
|
||||
m_active = m_allocator.newPage ();
|
||||
|
||||
header = reinterpret_cast <Header*> (m_active->allocate (bytesNeeded));
|
||||
}
|
||||
|
||||
header->page = m_active;
|
||||
|
||||
return reinterpret_cast <char*> (header) + headerBytes;
|
||||
}
|
||||
|
||||
private:
|
||||
FifoFreeStoreWithTLS& m_allocator;
|
||||
Page* m_active;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline FifoFreeStoreWithTLS::Page* FifoFreeStoreWithTLS::newPage ()
|
||||
{
|
||||
return new (m_pages->allocate ()) Page (m_pages->getPageBytes ());
|
||||
}
|
||||
|
||||
inline void FifoFreeStoreWithTLS::deletePage (Page* page)
|
||||
{
|
||||
// Safe, because each thread maintains its own active page.
|
||||
page->~Page ();
|
||||
PagedFreeStoreType::deallocate (page);
|
||||
}
|
||||
|
||||
FifoFreeStoreWithTLS::FifoFreeStoreWithTLS ()
|
||||
: m_pages (PagedFreeStoreType::getInstance ())
|
||||
{
|
||||
//bassert (m_pages->getPageBytes () >= sizeof (Page) + Memory::allocAlignBytes);
|
||||
}
|
||||
|
||||
FifoFreeStoreWithTLS::~FifoFreeStoreWithTLS ()
|
||||
{
|
||||
// Clean up this thread's data before we release
|
||||
// the reference to the global page allocator.
|
||||
m_tsp.reset (0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void* FifoFreeStoreWithTLS::allocate (const size_t bytes)
|
||||
{
|
||||
PerThreadData* data = m_tsp.get ();
|
||||
|
||||
if (!data)
|
||||
{
|
||||
data = new PerThreadData (this);
|
||||
m_tsp.reset (data);
|
||||
}
|
||||
|
||||
return data->allocate (bytes);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void FifoFreeStoreWithTLS::deallocate (void* p)
|
||||
{
|
||||
const size_t headerBytes = Memory::sizeAdjustedForAlignment (sizeof (Header));
|
||||
Header* const header = reinterpret_cast <Header*> (reinterpret_cast <char*> (p) - headerBytes);
|
||||
Page* const page = header->page;
|
||||
|
||||
if (page->release ())
|
||||
deletePage (page);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,69 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_FIFOFREESTOREWITHTLS_BEASTHEADER
|
||||
#define BEAST_FIFOFREESTOREWITHTLS_BEASTHEADER
|
||||
|
||||
#if BEAST_BOOST_IS_AVAILABLE
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Lock-free and mostly wait-free FIFO memory allocator.
|
||||
|
||||
This allocator is suitable for use with CallQueue and Listeners. It is
|
||||
expected that over time, deallocations will occur in roughly the same order
|
||||
as allocations.
|
||||
|
||||
@note This implementation uses Thread Local Storage to further improve
|
||||
performance. However, it requires boost style thread_specific_ptr.
|
||||
|
||||
@invariant allocate() and deallocate() are fully concurrent.
|
||||
|
||||
@invariant The ABA problem is handled automatically.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API FifoFreeStoreWithTLS
|
||||
{
|
||||
public:
|
||||
FifoFreeStoreWithTLS ();
|
||||
~FifoFreeStoreWithTLS ();
|
||||
|
||||
void* allocate (const size_t bytes);
|
||||
static void deallocate (void* const p);
|
||||
|
||||
private:
|
||||
typedef GlobalPagedFreeStore PagedFreeStoreType;
|
||||
struct Header;
|
||||
|
||||
class Page;
|
||||
|
||||
inline Page* newPage ();
|
||||
static inline void deletePage (Page* page);
|
||||
|
||||
private:
|
||||
class PerThreadData;
|
||||
boost::thread_specific_ptr <PerThreadData> m_tsp;
|
||||
|
||||
PagedFreeStoreType::Ptr m_pages;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,241 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// This precedes every allocation
|
||||
struct FifoFreeStoreWithoutTLS::Header
|
||||
{
|
||||
union
|
||||
{
|
||||
FifoFreeStoreWithoutTLS::Block* block; // backpointer to the page
|
||||
|
||||
char pad [Memory::allocAlignBytes];
|
||||
};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class FifoFreeStoreWithoutTLS::Block : public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit Block (const size_t bytes) : m_refs (1)
|
||||
{
|
||||
m_end = reinterpret_cast <char*> (this) + bytes;
|
||||
m_free = reinterpret_cast <char*> (
|
||||
Memory::pointerAdjustedForAlignment (this + 1));
|
||||
}
|
||||
|
||||
~Block ()
|
||||
{
|
||||
bassert (!m_refs.isSignaled ());
|
||||
}
|
||||
|
||||
inline void addref ()
|
||||
{
|
||||
m_refs.addref ();
|
||||
}
|
||||
|
||||
inline bool release ()
|
||||
{
|
||||
return m_refs.release ();
|
||||
}
|
||||
|
||||
enum Result
|
||||
{
|
||||
success, // successful allocation
|
||||
ignore, // disregard the block
|
||||
consumed // block is consumed (1 thread sees this)
|
||||
};
|
||||
|
||||
Result allocate (size_t bytes, void* pBlock)
|
||||
{
|
||||
bassert (bytes > 0);
|
||||
|
||||
Result result;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char* base = m_free.get ();
|
||||
|
||||
if (base)
|
||||
{
|
||||
char* p = Memory::pointerAdjustedForAlignment (base);
|
||||
char* free = p + bytes;
|
||||
|
||||
if (free <= m_end)
|
||||
{
|
||||
// Try to commit the allocation
|
||||
if (m_free.compareAndSet (free, base))
|
||||
{
|
||||
* (reinterpret_cast <void**> (pBlock)) = p;
|
||||
result = success;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Someone changed m_free, retry.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mark the block consumed.
|
||||
if (m_free.compareAndSet (0, base))
|
||||
{
|
||||
// Only one caller sees this, the rest get 'ignore'
|
||||
result = consumed;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Happens with another concurrent allocate(), retry.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Block is consumed, ignore it.
|
||||
result = ignore;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
AtomicCounter m_refs; // reference count
|
||||
AtomicPointer <char> m_free; // next free byte or 0 if inactive.
|
||||
char* m_end; // last free byte + 1
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline FifoFreeStoreWithoutTLS::Block* FifoFreeStoreWithoutTLS::newBlock ()
|
||||
{
|
||||
return new (m_pages->allocate ()) Block (m_pages->getPageBytes ());
|
||||
}
|
||||
|
||||
inline void FifoFreeStoreWithoutTLS::deleteBlock (Block* b)
|
||||
{
|
||||
// It is critical that we do not call the destructor,
|
||||
// because due to the lock-free implementation, a Block
|
||||
// can be accessed for a short time after it is deleted.
|
||||
/* b->~Block (); */ // DO NOT CALL!!!
|
||||
|
||||
PagedFreeStoreType::deallocate (b);
|
||||
}
|
||||
|
||||
FifoFreeStoreWithoutTLS::FifoFreeStoreWithoutTLS ()
|
||||
: m_pages (GlobalPagedFreeStore::getInstance ())
|
||||
{
|
||||
if (m_pages->getPageBytes () < sizeof (Block) + 256)
|
||||
Throw (Error ().fail (__FILE__, __LINE__, TRANS ("the block size is too small")));
|
||||
|
||||
m_active = newBlock ();
|
||||
}
|
||||
|
||||
FifoFreeStoreWithoutTLS::~FifoFreeStoreWithoutTLS ()
|
||||
{
|
||||
deleteBlock (m_active);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void* FifoFreeStoreWithoutTLS::allocate (const size_t bytes)
|
||||
{
|
||||
const size_t actual = sizeof (Header) + bytes;
|
||||
|
||||
if (actual > m_pages->getPageBytes ())
|
||||
Throw (Error ().fail (__FILE__, __LINE__, TRANS ("the memory request was too large")));
|
||||
|
||||
Header* h;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Get an active block.
|
||||
Block* b = m_active;
|
||||
|
||||
while (!b)
|
||||
{
|
||||
Thread::yield ();
|
||||
b = m_active;
|
||||
}
|
||||
|
||||
// (*) It is possible for the block to get a final release here
|
||||
// In this case it will have been put in the garbage, and
|
||||
// m_active will not match.
|
||||
|
||||
// Acquire a reference.
|
||||
b->addref ();
|
||||
|
||||
// Is it still active?
|
||||
if (m_active == b)
|
||||
{
|
||||
// Yes so try to allocate from it.
|
||||
const Block::Result result = b->allocate (actual, &h);
|
||||
|
||||
if (result == Block::success)
|
||||
{
|
||||
// Keep the reference and return the allocation.
|
||||
h->block = b;
|
||||
break;
|
||||
}
|
||||
else if (result == Block::consumed)
|
||||
{
|
||||
// Remove block from active.
|
||||
m_active = 0;
|
||||
|
||||
// Take away the reference we added
|
||||
b->release ();
|
||||
|
||||
// Take away the original active reference.
|
||||
if (b->release ())
|
||||
deleteBlock (b);
|
||||
|
||||
// Install a fresh empty active block.
|
||||
m_active = newBlock ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b->release ())
|
||||
deleteBlock (b);
|
||||
}
|
||||
|
||||
// Try again.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Block became inactive, so release our reference.
|
||||
b->release ();
|
||||
|
||||
// (*) It is possible for this to be a duplicate final release.
|
||||
}
|
||||
}
|
||||
|
||||
return h + 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void FifoFreeStoreWithoutTLS::deallocate (void* p)
|
||||
{
|
||||
Block* const b = (reinterpret_cast <Header*> (p) - 1)->block;
|
||||
|
||||
if (b->release ())
|
||||
deleteBlock (b);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_FIFOFREESTOREWITHOUTTLS_BEASTHEADER
|
||||
#define BEAST_FIFOFREESTOREWITHOUTTLS_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Lock-free FIFO memory allocator.
|
||||
|
||||
This allocator is suitable for use with CallQueue and Listeners. It is
|
||||
expected that over time, deallocations will occur in roughly the same order
|
||||
as allocations.
|
||||
|
||||
@note This version of the fifo free store uses less memory and doesn't require
|
||||
thread specific storage. However, it runs slower. The performance
|
||||
differences are negligible for desktop class applications.
|
||||
|
||||
@invariant allocate() and deallocate() are fully concurrent.
|
||||
|
||||
@invariant The ABA problem is handled automatically.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API FifoFreeStoreWithoutTLS
|
||||
{
|
||||
public:
|
||||
explicit FifoFreeStoreWithoutTLS ();
|
||||
~FifoFreeStoreWithoutTLS ();
|
||||
|
||||
void* allocate (const size_t bytes);
|
||||
static void deallocate (void* const p);
|
||||
|
||||
private:
|
||||
typedef GlobalPagedFreeStore PagedFreeStoreType;
|
||||
|
||||
struct Header;
|
||||
|
||||
class Block;
|
||||
|
||||
inline Block* newBlock ();
|
||||
static inline void deleteBlock (Block* b);
|
||||
|
||||
private:
|
||||
Block* volatile m_active;
|
||||
PagedFreeStoreType::Ptr m_pages;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,226 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#define LOG_GC 0
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// This is the upper limit on the amount of physical memory an instance of the
|
||||
// allocator will allow. Going over this limit means that consumers cannot keep
|
||||
// up with producers, and application logic should be re-examined.
|
||||
//
|
||||
// TODO: ENFORCE THIS GLOBALLY? MEASURE IN KILOBYTES AND FORCE KILOBYTE PAGE SIZES
|
||||
#define HARD_LIMIT 1
|
||||
const size_t hardLimitMegaBytes = 256;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Implementation notes
|
||||
|
||||
- There are two pools, the 'hot' pool and the 'cold' pool.
|
||||
|
||||
- When a new page is needed we pop from the 'fresh' stack of the hot pool.
|
||||
|
||||
- When a page is deallocated it is pushed to the 'garbage' stack of the hot pool.
|
||||
|
||||
- Every so often, a garbage collection is performed on a separate thread.
|
||||
During collection, fresh and garbage are swapped in the cold pool.
|
||||
Then, the hot and cold pools are atomically swapped.
|
||||
|
||||
*/
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct PagedFreeStore::Page : Pages::Node, LeakChecked <Page>
|
||||
{
|
||||
explicit Page (PagedFreeStore* const allocator)
|
||||
: m_allocator (*allocator)
|
||||
{
|
||||
}
|
||||
|
||||
PagedFreeStore& getAllocator () const
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
PagedFreeStore& m_allocator;
|
||||
};
|
||||
|
||||
inline void* PagedFreeStore::fromPage (Page* const p)
|
||||
{
|
||||
return reinterpret_cast <char*> (p) +
|
||||
Memory::sizeAdjustedForAlignment (sizeof (Page));
|
||||
}
|
||||
|
||||
inline PagedFreeStore::Page* PagedFreeStore::toPage (void* const p)
|
||||
{
|
||||
return reinterpret_cast <Page*> (
|
||||
(reinterpret_cast <char*> (p) -
|
||||
Memory::sizeAdjustedForAlignment (sizeof (Page))));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
PagedFreeStore::PagedFreeStore (const size_t pageBytes)
|
||||
: m_pageBytes (pageBytes)
|
||||
, m_pageBytesAvailable (pageBytes - Memory::sizeAdjustedForAlignment (sizeof (Page)))
|
||||
, m_newPagesLeft (int ((hardLimitMegaBytes * 1024 * 1024) / m_pageBytes))
|
||||
#if LOG_GC
|
||||
, m_swaps (0)
|
||||
#endif
|
||||
{
|
||||
m_hot = m_pool1;
|
||||
m_cold = m_pool2;
|
||||
|
||||
startOncePerSecond ();
|
||||
}
|
||||
|
||||
PagedFreeStore::~PagedFreeStore ()
|
||||
{
|
||||
endOncePerSecond ();
|
||||
|
||||
#if LOG_GC
|
||||
bassert (!m_used.isSignaled ());
|
||||
#endif
|
||||
|
||||
dispose (m_pool1);
|
||||
dispose (m_pool2);
|
||||
|
||||
#if LOG_GC
|
||||
bassert (!m_total.isSignaled ());
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void* PagedFreeStore::allocate ()
|
||||
{
|
||||
Page* page = m_hot->fresh->pop_front ();
|
||||
|
||||
if (!page)
|
||||
{
|
||||
#if HARD_LIMIT
|
||||
const bool exhausted = m_newPagesLeft.release ();
|
||||
|
||||
if (exhausted)
|
||||
Throw (Error ().fail (__FILE__, __LINE__,
|
||||
TRANS ("the limit of memory allocations was reached")));
|
||||
|
||||
#endif
|
||||
|
||||
void* storage = ::malloc (m_pageBytes);
|
||||
|
||||
if (!storage)
|
||||
Throw (Error ().fail (__FILE__, __LINE__,
|
||||
TRANS ("a memory allocation failed")));
|
||||
|
||||
page = new (storage) Page (this);
|
||||
|
||||
#if LOG_GC
|
||||
m_total.addref ();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LOG_GC
|
||||
m_used.addref ();
|
||||
#endif
|
||||
|
||||
return fromPage (page);
|
||||
}
|
||||
|
||||
void PagedFreeStore::deallocate (void* const p)
|
||||
{
|
||||
Page* const page = toPage (p);
|
||||
PagedFreeStore& allocator = page->getAllocator ();
|
||||
|
||||
allocator.m_hot->garbage->push_front (page);
|
||||
|
||||
#if LOG_GC
|
||||
allocator.m_used.release ();
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Perform garbage collection.
|
||||
//
|
||||
void PagedFreeStore::doOncePerSecond ()
|
||||
{
|
||||
// Physically free one page.
|
||||
// This will reduce the working set over time after a spike.
|
||||
{
|
||||
Page* page = m_cold->garbage->pop_front ();
|
||||
|
||||
if (page)
|
||||
{
|
||||
page->~Page ();
|
||||
::free (page);
|
||||
m_newPagesLeft.addref ();
|
||||
#ifdef LOG_GC
|
||||
m_total.release ();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
m_cold->fresh->swap (m_cold->garbage);
|
||||
|
||||
// Swap atomically with respect to m_hot
|
||||
Pool* temp = m_hot;
|
||||
m_hot = m_cold; // atomic
|
||||
m_cold = temp;
|
||||
|
||||
#if LOG_GC
|
||||
String s;
|
||||
s << "swap " << String (++m_swaps);
|
||||
s << " (" << String (m_used.get ()) << "/"
|
||||
<< String (m_total.get ()) << " of "
|
||||
<< String (m_newPagesLeft.get ()) << ")";
|
||||
Logger::outputDebugString (s);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PagedFreeStore::dispose (Pages& pages)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
Page* const page = pages.pop_front ();
|
||||
|
||||
if (page)
|
||||
{
|
||||
page->~Page ();
|
||||
::free (page);
|
||||
|
||||
#if LOG_GC
|
||||
m_total.release ();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PagedFreeStore::dispose (Pool& pool)
|
||||
{
|
||||
dispose (pool.fresh);
|
||||
dispose (pool.garbage);
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_PAGEDFREESTORE_BEASTHEADER
|
||||
#define BEAST_PAGEDFREESTORE_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Lock-free memory allocator for fixed size pages.
|
||||
|
||||
The ABA problem (http://en.wikipedia.org/wiki/ABA_problem) is avoided by
|
||||
treating freed pages as garbage, and performing a collection every second.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API PagedFreeStore : private OncePerSecond
|
||||
{
|
||||
public:
|
||||
explicit PagedFreeStore (const size_t pageBytes);
|
||||
~PagedFreeStore ();
|
||||
|
||||
// The available bytes per page is a little bit less
|
||||
// than requested in the constructor, due to overhead.
|
||||
//
|
||||
inline size_t getPageBytes () const
|
||||
{
|
||||
return m_pageBytesAvailable;
|
||||
}
|
||||
|
||||
inline void* allocate (const size_t bytes)
|
||||
{
|
||||
if (bytes > m_pageBytes)
|
||||
Throw (Error ().fail (__FILE__, __LINE__, "the size is too large"));
|
||||
|
||||
return allocate ();
|
||||
}
|
||||
|
||||
void* allocate ();
|
||||
static void deallocate (void* const p);
|
||||
|
||||
private:
|
||||
void* newPage ();
|
||||
void doOncePerSecond ();
|
||||
|
||||
private:
|
||||
struct Page;
|
||||
typedef LockFreeStack <Page> Pages;
|
||||
|
||||
struct Pool
|
||||
{
|
||||
CacheLine::Padded <Pages> fresh;
|
||||
CacheLine::Padded <Pages> garbage;
|
||||
};
|
||||
|
||||
static inline void* fromPage (Page* const p);
|
||||
static inline Page* toPage (void* const p);
|
||||
|
||||
void dispose (Pages& pages);
|
||||
void dispose (Pool& pool);
|
||||
|
||||
private:
|
||||
const size_t m_pageBytes;
|
||||
const size_t m_pageBytesAvailable;
|
||||
CacheLine::Aligned <Pool> m_pool1; // pair of pools
|
||||
CacheLine::Aligned <Pool> m_pool2;
|
||||
Pool* volatile m_cold; // pool which is cooling down
|
||||
Pool* volatile m_hot; // pool we are currently using
|
||||
AtomicCounter m_newPagesLeft; // limit of system allocations
|
||||
|
||||
#if 1
|
||||
int m_swaps;
|
||||
AtomicCounter m_total;
|
||||
AtomicCounter m_used;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,153 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
CallQueue::CallQueue (String name)
|
||||
: m_name (name)
|
||||
{
|
||||
}
|
||||
|
||||
CallQueue::~CallQueue ()
|
||||
{
|
||||
// Someone forget to close the queue.
|
||||
bassert (m_closed.isSignaled ());
|
||||
|
||||
// Can't destroy queue with unprocessed calls.
|
||||
bassert (m_queue.empty ());
|
||||
}
|
||||
|
||||
bool CallQueue::isAssociatedWithCurrentThread () const
|
||||
{
|
||||
return Thread::getCurrentThreadId () == m_id;
|
||||
}
|
||||
|
||||
// Adds a call to the queue of execution.
|
||||
void CallQueue::queuep (Work* c)
|
||||
{
|
||||
// If this goes off it means calls are being made after the
|
||||
// queue is closed, and probably there is no one around to
|
||||
// process it.
|
||||
bassert (!m_closed.isSignaled ());
|
||||
|
||||
if (m_queue.push_back (c))
|
||||
signal ();
|
||||
}
|
||||
|
||||
// Append the Work to the queue. If this call is made from the same
|
||||
// thread as the last thread that called synchronize(), then the call
|
||||
// will execute synchronously.
|
||||
//
|
||||
void CallQueue::callp (Work* c)
|
||||
{
|
||||
queuep (c);
|
||||
|
||||
// If we are called on the process thread and we are not
|
||||
// recursed into doSynchronize, then process the queue. This
|
||||
// makes calls from the process thread synchronous.
|
||||
//
|
||||
// NOTE: The value of isBeingSynchronized is invalid/volatile unless
|
||||
// this thread is the last process thread.
|
||||
//
|
||||
// NOTE: There is a small window of opportunity where we
|
||||
// might get an undesired synchronization if new thread
|
||||
// calls synchronize() concurrently.
|
||||
//
|
||||
if (isAssociatedWithCurrentThread () &&
|
||||
m_isBeingSynchronized.trySignal ())
|
||||
{
|
||||
doSynchronize ();
|
||||
|
||||
m_isBeingSynchronized.reset ();
|
||||
}
|
||||
}
|
||||
|
||||
bool CallQueue::synchronize ()
|
||||
{
|
||||
bool did_something;
|
||||
|
||||
// Detect recursion into doSynchronize(), and
|
||||
// break ties for concurrent calls atomically.
|
||||
//
|
||||
if (m_isBeingSynchronized.trySignal ())
|
||||
{
|
||||
// Remember this thread.
|
||||
m_id = Thread::getCurrentThreadId ();
|
||||
|
||||
did_something = doSynchronize ();
|
||||
|
||||
m_isBeingSynchronized.reset ();
|
||||
}
|
||||
else
|
||||
{
|
||||
did_something = false;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
// Can still have pending calls, just can't put new ones in.
|
||||
void CallQueue::close ()
|
||||
{
|
||||
m_closed.signal ();
|
||||
|
||||
synchronize ();
|
||||
}
|
||||
|
||||
// Process everything in the queue. The list of pending calls is
|
||||
// acquired atomically. New calls may enter the queue while we are
|
||||
// processing.
|
||||
//
|
||||
// Returns true if any functors were called.
|
||||
//
|
||||
bool CallQueue::doSynchronize ()
|
||||
{
|
||||
bool did_something;
|
||||
|
||||
// Reset since we are emptying the queue. Since we loop
|
||||
// until the queue is empty, it is possible for us to exit
|
||||
// this function with an empty queue and signaled state.
|
||||
//
|
||||
reset ();
|
||||
|
||||
Work* call = m_queue.pop_front ();
|
||||
|
||||
if (call)
|
||||
{
|
||||
did_something = true;
|
||||
|
||||
// This method of processing one at a time has the desired
|
||||
// side effect of synchronizing nested calls to us from a functor.
|
||||
//
|
||||
for (;;)
|
||||
{
|
||||
call->operator () ();
|
||||
delete call;
|
||||
|
||||
call = m_queue.pop_front ();
|
||||
|
||||
if (call == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
did_something = false;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
@@ -1,506 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CALLQUEUE_BEASTHEADER
|
||||
#define BEAST_CALLQUEUE_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A FIFO for calling functors asynchronously.
|
||||
|
||||
This object is an alternative to traditional locking techniques used to
|
||||
implement concurrent systems. Instead of acquiring a mutex to change shared
|
||||
data, a functor is queued for later execution (usually on another thread). The
|
||||
execution of the functor applies the transformation to the shared state that
|
||||
was formerly performed within a lock (i.e. CriticalSection).
|
||||
|
||||
For read operations on shared data, instead of acquiring a mutex and
|
||||
accessing the data directly, copies are made (one for each thread), and the
|
||||
thread accesses its copy without acquiring a lock. One thread owns the master
|
||||
copy of the shared state. Requests for changing shared state are made by other
|
||||
threads by posting functors to the master thread's CallQueue. The master
|
||||
thread notifies other threads of changes by posting functors to their
|
||||
respective associated CallQueue, using the Listeners interface.
|
||||
|
||||
The purpose of the functor is to encapsulate one mutation of shared state to
|
||||
guarantee progress towards a consensus of the concurrent data among
|
||||
participating threads. Functors should execute quickly, ideally in constant
|
||||
time. Dynamically allocated objects of class type passed as functor parameters
|
||||
should, in general, be reference counted. The ConcurrentObject class is ideal
|
||||
for meeting this requirement, and has the additional benefit that the workload
|
||||
of deletion is performed on a separate, provided thread. This queue is not a
|
||||
replacement for a thread pool or job queue type system.
|
||||
|
||||
A CallQueue is considered signaled when one or more functors are present.
|
||||
Functors are executed during a call to synchronize(). The operation of
|
||||
executing functors via the call to synchronize() is called synchronizing
|
||||
the queue. It can more generally be thought of as synchronizing multiple
|
||||
copies of shared data between threads.
|
||||
|
||||
Although there is some extra work required to set up and maintain this
|
||||
system, the benefits are significant. Since shared data is only synchronized
|
||||
at well defined times, the programmer can reason and make strong statements
|
||||
about the correctness of the concurrent system. For example, if an
|
||||
AudioIODeviceCallback synchronizes the CallQueue only at the beginning of its
|
||||
execution, it is guaranteed that shared data will remain the same throughout
|
||||
the remainder of the function.
|
||||
|
||||
Because shared data is accessed for reading without a lock, upper bounds
|
||||
on the run time performance can easily be calculated and assured. Compare
|
||||
this with the use of a mutex - the run time performance experiences a
|
||||
combinatorial explosion of possibilities depending on the complex interaction
|
||||
of multiple threads.
|
||||
|
||||
Since a CallQueue is almost always used to invoke parameterized member
|
||||
functions of objects, the call() function comes in a variety of convenient
|
||||
forms to make usage easy:
|
||||
|
||||
@code
|
||||
|
||||
void func1 (int);
|
||||
|
||||
struct Object
|
||||
{
|
||||
void func2 (void);
|
||||
void func3 (String name);
|
||||
|
||||
static void func4 ();
|
||||
};
|
||||
|
||||
CallQueue fifo ("Example");
|
||||
|
||||
void example ()
|
||||
{
|
||||
fifo.call (func1, 42); // same as: func1 (42)
|
||||
|
||||
Object* object = new Object;
|
||||
|
||||
fifo.call (&Object::func2, object); // same as: object->func2 ()
|
||||
|
||||
fifo.call (&Object::func3, // same as: object->funcf ("Label")
|
||||
object,
|
||||
"Label");
|
||||
|
||||
fifo.call (&Object::func4); // even static members can be called.
|
||||
|
||||
fifo.callf (functional::bind (&Object::func2, // same as: object->func2 ()
|
||||
object));
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@invariant Functors can be added from any thread at any time, to any queue
|
||||
which is not closed.
|
||||
|
||||
@invariant When synchronize() is called, functors are called and deleted.
|
||||
|
||||
@invariant The thread from which synchronize() is called is considered the
|
||||
thread associated with the CallQueue.
|
||||
|
||||
@invariant Functors queued by the same thread always execute in the same
|
||||
order they were queued.
|
||||
|
||||
@invariant Functors are guaranteed to execute. It is an error if the
|
||||
CallQueue is deleted while there are functors in it.
|
||||
|
||||
Normally, you will not use CallQueue directly, but one of its subclasses
|
||||
instead. The CallQueue is one of a handful of objects that work together to
|
||||
implement this system of concurrent data access.
|
||||
|
||||
For performance considerations, this implementation is wait-free for
|
||||
producers and mostly wait-free for consumers. It also uses a lock-free
|
||||
and wait-free (in the fast path) custom memory allocator.
|
||||
|
||||
@see GuiCallQueue, ManualCallQueue, MessageThread, ThreadWithCallQueue
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API CallQueue
|
||||
{
|
||||
public:
|
||||
//============================================================================
|
||||
|
||||
/** Type of allocator to use.
|
||||
|
||||
@internal
|
||||
*/
|
||||
typedef FifoFreeStoreType AllocatorType;
|
||||
|
||||
/** Abstract nullary functor in a @ref CallQueue.
|
||||
|
||||
Custom implementations may derive from this object for efficiency instead
|
||||
of using the automatic binding functions.
|
||||
*/
|
||||
class Work : public LockFreeQueue <Work>::Node,
|
||||
public AllocatedBy <AllocatorType>
|
||||
{
|
||||
public:
|
||||
virtual ~Work () { }
|
||||
|
||||
/** Calls the functor.
|
||||
|
||||
This executes during the queue's call to synchronize().
|
||||
*/
|
||||
virtual void operator () () = 0;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
/** Create the CallQueue.
|
||||
|
||||
The queue starts out open and empty.
|
||||
|
||||
@param name A string to identify the queue during debugging.
|
||||
*/
|
||||
explicit CallQueue (String name);
|
||||
|
||||
/** Destroy the CallQueue.
|
||||
|
||||
@invariant Destroying a queue that contains functors results in undefined
|
||||
behavior.
|
||||
|
||||
@note It is customary to call close() on the CallQueue early in the
|
||||
shutdown process to catch functors going into the queue late.
|
||||
*/
|
||||
virtual ~CallQueue ();
|
||||
|
||||
//============================================================================
|
||||
|
||||
/** Add a functor and possibly synchronize.
|
||||
|
||||
Use this when you want to perform the bind yourself.
|
||||
|
||||
@param f The functor to add, typically the return value of a call
|
||||
to bind().
|
||||
|
||||
@see call
|
||||
*/
|
||||
template <class Functor>
|
||||
void callf (Functor f)
|
||||
{
|
||||
callp (new (m_allocator) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
/** Add a function call and possibly synchronize.
|
||||
|
||||
Parameters are evaluated immediately and added to the queue as a packaged
|
||||
functor. If the current thread of execution is the same as the thread
|
||||
associated with the CallQueue, synchronize() is called automatically. This
|
||||
behavior can be avoided by using queue() instead.
|
||||
|
||||
@param f The function to call followed by up to eight parameters,
|
||||
evaluated immediately. The parameter list must match the function
|
||||
signature. For class member functions, the first argument must be a
|
||||
pointer to the class object.
|
||||
|
||||
@see queue
|
||||
|
||||
@todo Provide an example of when synchronize() is needed in call().
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Fn>
|
||||
void call (Fn f)
|
||||
{ callf (functional::bind (f)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Fn, class T1>
|
||||
void call (Fn f, T1 t1)
|
||||
{ callf (functional::bind (f, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Fn, class T1, class T2>
|
||||
void call (Fn f, T1 t1, T2 t2)
|
||||
{ callf (functional::bind (f, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Fn, class T1, class T2, class T3>
|
||||
void call (Fn f, T1 t1, T2 t2, T3 t3)
|
||||
{ callf (functional::bind (f, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Fn, class T1, class T2, class T3, class T4>
|
||||
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5>
|
||||
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void call (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/** Add a functor without synchronizing.
|
||||
|
||||
Use this when you want to perform the bind yourself.
|
||||
|
||||
@param f The functor to add, typically the return value of a call
|
||||
to bind().
|
||||
|
||||
@see queue
|
||||
*/
|
||||
template <class Functor>
|
||||
void queuef (Functor f)
|
||||
{
|
||||
queuep (new (m_allocator) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
/** Add a function call without synchronizing.
|
||||
|
||||
Parameters are evaluated immediately, then the resulting functor is added
|
||||
to the queue. This is used to postpone the call to synchronize() when
|
||||
there would be adverse side effects to executing the function immediately.
|
||||
In this example, we use queue() instead of call() to avoid a deadlock:
|
||||
|
||||
@code
|
||||
|
||||
struct SharedState; // contains data shared between threads
|
||||
|
||||
SharedData <SharedState> sharedState;
|
||||
|
||||
void stateChanged ()
|
||||
{
|
||||
SharedData <SharedState>::ReadAccess state (sharedState);
|
||||
|
||||
// (read state)
|
||||
}
|
||||
|
||||
CallQueue fifo;
|
||||
|
||||
void changeState ()
|
||||
{
|
||||
SharedData <State>::WriteAccess state (sharedState);
|
||||
|
||||
// (read and write state)
|
||||
|
||||
fifo.call (&stateChanged); // BUG: DEADLOCK because of the implicit synchronize().
|
||||
|
||||
fifo.queue (&stateChanged); // Okay, synchronize() will be called later,
|
||||
// after the write lock is released.
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@param f The function to call followed by up to eight parameters,
|
||||
evaluated immediately. The parameter list must match the
|
||||
function signature. For non-static class member functions,
|
||||
the first argument must be a pointer an instance of the class.
|
||||
|
||||
@see call
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Fn>
|
||||
void queue (Fn f)
|
||||
{ queuef (functional::bind (f)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Fn, class T1>
|
||||
void queue (Fn f, T1 t1)
|
||||
{ queuef (functional::bind (f, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Fn, class T1, class T2>
|
||||
void queue (Fn f, T1 t1, T2 t2)
|
||||
{ queuef (functional::bind (f, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Fn, class T1, class T2, class T3>
|
||||
void queue (Fn f, T1 t1, T2 t2, T3 t3)
|
||||
{ queuef (functional::bind (f, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Fn, class T1, class T2, class T3, class T4>
|
||||
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ queuef (functional::bind (f, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5>
|
||||
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ queuef (functional::bind (f, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ queuef (functional::bind (f, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ queuef (functional::bind (f, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void queue (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ queuef (functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
protected:
|
||||
//============================================================================
|
||||
/** Synchronize the queue.
|
||||
|
||||
A synchronize operation calls all functors in the queue. If a functor
|
||||
causes additional functors to be added, they are eventually executed
|
||||
before synchronize() returns. Derived class call this when the queue is
|
||||
signaled, and optionally at any other time. Calling this function from
|
||||
more than one thread simultaneously is undefined.
|
||||
|
||||
@return true if any functors were executed.
|
||||
*/
|
||||
bool synchronize ();
|
||||
|
||||
/** Close the queue.
|
||||
|
||||
Functors may not be added after this routine is called. This is used for
|
||||
diagnostics, to track down spurious calls during application shutdown
|
||||
or exit. Derived classes may call this if the appropriate time is known.
|
||||
|
||||
The queue is synchronized after it is closed.
|
||||
*/
|
||||
void close ();
|
||||
|
||||
/** Called when the queue becomes signaled.
|
||||
|
||||
A queue is signaled on the transition from empty to non-empty. Derived
|
||||
classes implement this function to perform a notification so that
|
||||
synchronize() will be called. For example, by triggering a WaitableEvent.
|
||||
|
||||
@note Due to the implementation the queue can remain signaled for one
|
||||
extra cycle. This does not happen under load and is not an issue
|
||||
in practice.
|
||||
*/
|
||||
virtual void signal () = 0;
|
||||
|
||||
/** Called when the queue is reset.
|
||||
|
||||
A queue is reset when it was previously signaled and then becomes empty
|
||||
as a result of a call to synchronize.
|
||||
*/
|
||||
virtual void reset () = 0;
|
||||
|
||||
public:
|
||||
//============================================================================
|
||||
|
||||
/** Add a raw call.
|
||||
|
||||
@internal
|
||||
|
||||
Custom implementations use this to control the allocation.
|
||||
|
||||
@param c The call to add. The memory must come from the allocator.
|
||||
*/
|
||||
void callp (Work* c);
|
||||
|
||||
/** Queue a raw call.
|
||||
|
||||
Custom implementations use this to control the allocation.
|
||||
|
||||
@param c The call to add. The memory must come from the allocator.
|
||||
*/
|
||||
void queuep (Work* c);
|
||||
|
||||
/** Retrieve the allocator.
|
||||
|
||||
@return The allocator to use when allocating a raw Work object.
|
||||
*/
|
||||
inline AllocatorType& getAllocator ()
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
/** See if the caller is on the association thread.
|
||||
|
||||
@return `true` if the calling thread of execution is associated with the
|
||||
queue.
|
||||
*/
|
||||
bool isAssociatedWithCurrentThread () const;
|
||||
|
||||
/** See if the queue is being synchronized.
|
||||
|
||||
This is used for diagnostics.
|
||||
|
||||
@note This must be called from the associated thread or else the return
|
||||
value is undefined.
|
||||
|
||||
@return `true` if the call stack contains synchronize() for this queue.
|
||||
*/
|
||||
bool isBeingSynchronized () const
|
||||
{
|
||||
return m_isBeingSynchronized.isSignaled ();
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Functor>
|
||||
class CallType : public Work
|
||||
{
|
||||
public:
|
||||
explicit CallType (Functor f) : m_f (f) { }
|
||||
void operator () ()
|
||||
{
|
||||
m_f ();
|
||||
}
|
||||
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
bool doSynchronize ();
|
||||
|
||||
private:
|
||||
String const m_name;
|
||||
Thread::ThreadID m_id;
|
||||
LockFreeQueue <Work> m_queue;
|
||||
AtomicFlag m_closed;
|
||||
AtomicFlag m_isBeingSynchronized;
|
||||
AllocatorType m_allocator;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,79 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CONCURRENTOBJECT_BEASTHEADER
|
||||
#define BEAST_CONCURRENTOBJECT_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A reference counted object with overridable destroy behavior.
|
||||
|
||||
This is a reference counted object compatible with
|
||||
ReferenceCountedObjectPtr. When the last reference is removed, the
|
||||
object is queued for deletion on a separate, provided thread. On
|
||||
program exit the thread will clean itself up - no other action is
|
||||
required.
|
||||
|
||||
This class is useful for offloading the deletion work of "deep" objects
|
||||
shared by multiple threads: objects containing complex members, or a
|
||||
hierarchy of allocated structures. For example, a ValueTree. The problem
|
||||
of performing heavyweight memory or cleanup operations from either an
|
||||
AudioIODeviceCallback or the message thread is avoided.
|
||||
|
||||
The deletion behavior can be overriden by providing a replacement
|
||||
for destroyConcurrentObject().
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API ConcurrentObject : Uncopyable
|
||||
{
|
||||
public:
|
||||
inline void incReferenceCount () noexcept
|
||||
{
|
||||
m_refs.addref ();
|
||||
}
|
||||
|
||||
inline void decReferenceCount () noexcept
|
||||
{
|
||||
if (m_refs.release ())
|
||||
destroyConcurrentObject ();
|
||||
}
|
||||
|
||||
protected:
|
||||
ConcurrentObject ();
|
||||
|
||||
virtual ~ConcurrentObject ();
|
||||
|
||||
/** Delete the object.
|
||||
|
||||
This function is called when the reference count drops to zero. The
|
||||
default implementation performs the delete on a separate, provided thread
|
||||
that cleans up after itself on exit.
|
||||
*/
|
||||
virtual void destroyConcurrentObject ();
|
||||
|
||||
protected:
|
||||
class Deleter;
|
||||
|
||||
private:
|
||||
AtomicCounter m_refs;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
InterruptibleThread::ThreadHelper::ThreadHelper (String name,
|
||||
InterruptibleThread* owner)
|
||||
: Thread (name)
|
||||
, m_owner (owner)
|
||||
{
|
||||
}
|
||||
|
||||
InterruptibleThread* InterruptibleThread::ThreadHelper::getOwner () const
|
||||
{
|
||||
return m_owner;
|
||||
}
|
||||
|
||||
void InterruptibleThread::ThreadHelper::run ()
|
||||
{
|
||||
m_owner->run ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
InterruptibleThread::InterruptibleThread (String name)
|
||||
: m_thread (name, this)
|
||||
, m_entryPoint (nullptr)
|
||||
, m_state (stateRun)
|
||||
{
|
||||
}
|
||||
|
||||
InterruptibleThread::~InterruptibleThread ()
|
||||
{
|
||||
m_runEvent.signal ();
|
||||
|
||||
join ();
|
||||
}
|
||||
|
||||
void InterruptibleThread::start (EntryPoint* const entryPoint)
|
||||
{
|
||||
m_entryPoint = entryPoint;
|
||||
|
||||
m_thread.startThread ();
|
||||
|
||||
// Prevent data race with member variables
|
||||
//
|
||||
m_runEvent.signal ();
|
||||
}
|
||||
|
||||
void InterruptibleThread::join ()
|
||||
{
|
||||
m_thread.stopThread (-1);
|
||||
}
|
||||
|
||||
bool InterruptibleThread::wait (int milliSeconds)
|
||||
{
|
||||
// Can only be called from the corresponding thread of execution.
|
||||
//
|
||||
bassert (isTheCurrentThread ());
|
||||
|
||||
bool interrupted = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
bassert (m_state != stateWait);
|
||||
|
||||
// See if we are interrupted
|
||||
//
|
||||
if (m_state.tryChangeState (stateInterrupt, stateRun))
|
||||
{
|
||||
// We were interrupted, state is changed to Run. Caller must run now.
|
||||
//
|
||||
interrupted = true;
|
||||
break;
|
||||
}
|
||||
else if (m_state.tryChangeState (stateRun, stateWait) ||
|
||||
m_state.tryChangeState (stateReturn, stateWait))
|
||||
{
|
||||
// Transitioned to wait. Caller must wait now.
|
||||
//
|
||||
interrupted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!interrupted)
|
||||
{
|
||||
interrupted = m_thread.wait (milliSeconds);
|
||||
|
||||
if (!interrupted)
|
||||
{
|
||||
if (m_state.tryChangeState (stateWait, stateRun))
|
||||
{
|
||||
interrupted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bassert (m_state == stateInterrupt);
|
||||
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return interrupted;
|
||||
}
|
||||
|
||||
void InterruptibleThread::interrupt ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
int const state = m_state;
|
||||
|
||||
if (state == stateInterrupt ||
|
||||
state == stateReturn ||
|
||||
m_state.tryChangeState (stateRun, stateInterrupt))
|
||||
{
|
||||
// Thread will see this at next interruption point.
|
||||
//
|
||||
break;
|
||||
}
|
||||
else if (m_state.tryChangeState (stateWait, stateRun))
|
||||
{
|
||||
m_thread.notify ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InterruptibleThread::interruptionPoint ()
|
||||
{
|
||||
// Can only be called from the thread of execution.
|
||||
//
|
||||
bassert (isTheCurrentThread ());
|
||||
|
||||
if (m_state == stateWait)
|
||||
{
|
||||
// It is impossible for this function to be called while in the wait state.
|
||||
//
|
||||
Throw (Error ().fail (__FILE__, __LINE__));
|
||||
}
|
||||
else if (m_state == stateReturn)
|
||||
{
|
||||
// If this goes off it means the thread called the
|
||||
// interruption a second time after already getting interrupted.
|
||||
//
|
||||
Throw (Error ().fail (__FILE__, __LINE__));
|
||||
}
|
||||
|
||||
bool const interrupted = m_state.tryChangeState (stateInterrupt, stateRun);
|
||||
|
||||
return interrupted;
|
||||
}
|
||||
|
||||
InterruptibleThread::id InterruptibleThread::getId () const
|
||||
{
|
||||
return m_threadId;
|
||||
}
|
||||
|
||||
bool InterruptibleThread::isTheCurrentThread () const
|
||||
{
|
||||
return m_thread.getCurrentThreadId () == m_threadId;
|
||||
}
|
||||
|
||||
void InterruptibleThread::setPriority (int priority)
|
||||
{
|
||||
m_thread.setPriority (priority);
|
||||
}
|
||||
|
||||
InterruptibleThread* InterruptibleThread::getCurrentThread ()
|
||||
{
|
||||
InterruptibleThread* result = nullptr;
|
||||
|
||||
Thread* const thread = Thread::getCurrentThread ();
|
||||
|
||||
if (thread != nullptr)
|
||||
{
|
||||
ThreadHelper* const helper = dynamic_cast <ThreadHelper*> (thread);
|
||||
|
||||
bassert (helper != nullptr);
|
||||
|
||||
result = helper->getOwner ();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void InterruptibleThread::run ()
|
||||
{
|
||||
m_threadId = m_thread.getThreadId ();
|
||||
|
||||
m_runEvent.wait ();
|
||||
|
||||
m_entryPoint->threadRun ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool CurrentInterruptibleThread::interruptionPoint ()
|
||||
{
|
||||
bool interrupted = false;
|
||||
|
||||
InterruptibleThread* const interruptibleThread (InterruptibleThread::getCurrentThread ());
|
||||
|
||||
bassert (interruptibleThread != nullptr);
|
||||
|
||||
interrupted = interruptibleThread->interruptionPoint ();
|
||||
|
||||
return interrupted;
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_INTERRUPTIBLETHREAD_BEASTHEADER
|
||||
#define BEAST_INTERRUPTIBLETHREAD_BEASTHEADER
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A thread with soft interruption support.
|
||||
|
||||
The thread must periodically call interruptionPoint(), which returns `true`
|
||||
the first time an interruption has occurred since the last call to
|
||||
interruptionPoint().
|
||||
|
||||
To create a thread, derive your class from InterruptibleThread::EntryPoint
|
||||
and implement the threadRun() function. Then, call run() with your object.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API InterruptibleThread
|
||||
{
|
||||
public:
|
||||
/** InterruptibleThread entry point.
|
||||
*/
|
||||
class EntryPoint
|
||||
{
|
||||
public:
|
||||
virtual ~EntryPoint () { }
|
||||
|
||||
virtual void threadRun () = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef Thread::ThreadID id;
|
||||
|
||||
/** Construct an interruptible thread.
|
||||
|
||||
The name is used for debugger diagnostics.
|
||||
|
||||
@param name The name of the thread.
|
||||
*/
|
||||
explicit InterruptibleThread (String name);
|
||||
|
||||
/** Destroy the interruptible thread.
|
||||
|
||||
This will signal an interrupt and wait until the thread exits.
|
||||
*/
|
||||
~InterruptibleThread ();
|
||||
|
||||
/** Start the thread.
|
||||
*/
|
||||
void start (EntryPoint* const entryPoint);
|
||||
|
||||
/** Wait for the thread to exit.
|
||||
*/
|
||||
void join ();
|
||||
|
||||
/** Wait for interrupt or timeout.
|
||||
|
||||
This call blocks until the thread is interrupted, or until the timeout
|
||||
expires if milliSeconds is non-negative.
|
||||
|
||||
May only be called by the thread of execution.
|
||||
|
||||
@param milliSeconds The amount of time to wait. Negative values mean
|
||||
no timeout.
|
||||
|
||||
@return `true` if the interrupt occurred, or `false` if the
|
||||
timeout expired.
|
||||
*/
|
||||
bool wait (int milliSeconds = -1);
|
||||
|
||||
/** Interrupt the thread of execution.
|
||||
|
||||
This can be called from any thread.
|
||||
*/
|
||||
void interrupt ();
|
||||
|
||||
/** Determine if an interruption is requested.
|
||||
|
||||
After the function returns `true`, the interrupt status is cleared.
|
||||
Subsequent calls will return `false` until another interrupt is requested.
|
||||
|
||||
May only be called by the thread of execution.
|
||||
|
||||
@see CurrentInterruptibleThread::interruptionPoint
|
||||
|
||||
@return `true` if an interrupt was requested.
|
||||
*/
|
||||
bool interruptionPoint ();
|
||||
|
||||
/** Get the ID of the associated thread.
|
||||
|
||||
@return The ID of the thread.
|
||||
*/
|
||||
id getId () const;
|
||||
|
||||
/** Determine if this is the thread of execution.
|
||||
|
||||
@note The return value is undefined if the thread is not running.
|
||||
|
||||
@return `true` if the caller is this thread of execution.
|
||||
*/
|
||||
bool isTheCurrentThread () const;
|
||||
|
||||
/** Adjust the thread priority.
|
||||
|
||||
@note This only affects some platforms.
|
||||
|
||||
@param priority A number from 0..10
|
||||
*/
|
||||
void setPriority (int priority);
|
||||
|
||||
/** Get the InterruptibleThread for the thread of execution.
|
||||
|
||||
This will return `nullptr` when called from the message thread, or from
|
||||
a thread of execution that is not an InterruptibleThread.
|
||||
*/
|
||||
static InterruptibleThread* getCurrentThread ();
|
||||
|
||||
private:
|
||||
class ThreadHelper : public Thread
|
||||
{
|
||||
public:
|
||||
ThreadHelper (String name, InterruptibleThread* owner);
|
||||
|
||||
InterruptibleThread* getOwner () const;
|
||||
|
||||
void run ();
|
||||
|
||||
private:
|
||||
InterruptibleThread* const m_owner;
|
||||
};
|
||||
|
||||
void run ();
|
||||
|
||||
ThreadHelper m_thread;
|
||||
EntryPoint* m_entryPoint;
|
||||
WaitableEvent m_runEvent;
|
||||
id m_threadId;
|
||||
|
||||
enum
|
||||
{
|
||||
stateRun,
|
||||
stateInterrupt,
|
||||
stateReturn,
|
||||
stateWait
|
||||
};
|
||||
|
||||
AtomicState m_state;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Global operations on the current InterruptibleThread.
|
||||
|
||||
Calling members of the class from a thread of execution which is not an
|
||||
InterruptibleThread results in undefined behavior.
|
||||
*/
|
||||
class CurrentInterruptibleThread
|
||||
{
|
||||
public:
|
||||
/** Call the current thread's interrupt point function.
|
||||
*/
|
||||
static bool interruptionPoint ();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,763 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// CallQueue item to process a Call for a particular listener.
|
||||
// This is used to avoid bind overhead.
|
||||
//
|
||||
class ListenersBase::CallWork : public CallQueue::Work
|
||||
{
|
||||
public:
|
||||
inline CallWork (ListenersBase::Call* const c, void* const listener)
|
||||
: m_call (c), m_listener (listener)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () ()
|
||||
{
|
||||
m_call->operator () (m_listener);
|
||||
}
|
||||
|
||||
private:
|
||||
ListenersBase::Call::Ptr m_call;
|
||||
void* const m_listener;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// CallQueue item to process a Call for a group.
|
||||
// This is used to avoid bind overhead.
|
||||
//
|
||||
class ListenersBase::GroupWork : public CallQueue::Work
|
||||
{
|
||||
public:
|
||||
inline GroupWork (Group* group,
|
||||
ListenersBase::Call* c,
|
||||
const timestamp_t timestamp)
|
||||
: m_group (group)
|
||||
, m_call (c)
|
||||
, m_timestamp (timestamp)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () ()
|
||||
{
|
||||
m_group->do_call (m_call, m_timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
Group::Ptr m_group;
|
||||
ListenersBase::Call::Ptr m_call;
|
||||
const timestamp_t m_timestamp;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// CallQueue item to process a call for a particular listener.
|
||||
// This is used to avoid bind overhead.
|
||||
//
|
||||
class ListenersBase::GroupWork1 : public CallQueue::Work
|
||||
{
|
||||
public:
|
||||
inline GroupWork1 (Group* group,
|
||||
ListenersBase::Call* c,
|
||||
const timestamp_t timestamp,
|
||||
void* const listener)
|
||||
: m_group (group)
|
||||
, m_call (c)
|
||||
, m_timestamp (timestamp)
|
||||
, m_listener (listener)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () ()
|
||||
{
|
||||
m_group->do_call1 (m_call, m_timestamp, m_listener);
|
||||
}
|
||||
|
||||
private:
|
||||
Group::Ptr m_group;
|
||||
ListenersBase::Call::Ptr m_call;
|
||||
const timestamp_t m_timestamp;
|
||||
void* const m_listener;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// A Proxy maintains a list of Entry.
|
||||
// Each Entry holds a group and the current Call (which can be updated).
|
||||
//
|
||||
struct ListenersBase::Proxy::Entry : Entries::Node,
|
||||
SharedObject,
|
||||
AllocatedBy <AllocatorType>
|
||||
{
|
||||
typedef SharedObjectPtr <Entry> Ptr;
|
||||
|
||||
explicit Entry (Group* g)
|
||||
: group (g)
|
||||
{
|
||||
}
|
||||
|
||||
~Entry ()
|
||||
{
|
||||
bassert (call.get () == 0);
|
||||
}
|
||||
|
||||
Group::Ptr group;
|
||||
AtomicPointer <Call> call;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// A Group maintains a list of Entry.
|
||||
//
|
||||
struct ListenersBase::Group::Entry : List <Entry>::Node,
|
||||
AllocatedBy <AllocatorType>
|
||||
{
|
||||
Entry (void* const l, const timestamp_t t)
|
||||
: listener (l)
|
||||
, timestamp (t)
|
||||
{
|
||||
}
|
||||
|
||||
void* const listener;
|
||||
const timestamp_t timestamp;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Group
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// - A list of listeners associated with the same CallQueue.
|
||||
//
|
||||
// - The list is only iterated on the CallQueue's thread.
|
||||
//
|
||||
// - It is safe to add or remove listeners from the group
|
||||
// at any time.
|
||||
//
|
||||
|
||||
ListenersBase::Group::Group (CallQueue& callQueue)
|
||||
: m_fifo (callQueue)
|
||||
, m_listener (0)
|
||||
{
|
||||
}
|
||||
|
||||
ListenersBase::Group::~Group ()
|
||||
{
|
||||
// If this goes off it means a Listener forgot to remove itself.
|
||||
bassert (m_list.empty ());
|
||||
|
||||
// shouldn't be deleting group during a call
|
||||
bassert (m_listener == 0);
|
||||
}
|
||||
|
||||
// Add the listener with the given timestamp.
|
||||
// The listener will only get calls with higher timestamps.
|
||||
// The caller must prevent duplicates.
|
||||
//
|
||||
void ListenersBase::Group::add (void* listener,
|
||||
const timestamp_t timestamp,
|
||||
AllocatorType& allocator)
|
||||
{
|
||||
ReadWriteMutex::ScopedWriteLockType lock (m_mutex);
|
||||
|
||||
bassert (!contains (listener));
|
||||
|
||||
// Should never be able to get here while in call()
|
||||
bassert (m_listener == 0);
|
||||
|
||||
// Add the listener and remember the time stamp so we don't
|
||||
// send it calls that were queued earlier than the add().
|
||||
m_list.push_back (*new (allocator) Entry (listener, timestamp));
|
||||
}
|
||||
|
||||
// Removes the listener from the group if it exists.
|
||||
// Returns true if the listener was removed.
|
||||
//
|
||||
bool ListenersBase::Group::remove (void* listener)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
ReadWriteMutex::ScopedWriteLockType lock (m_mutex);
|
||||
|
||||
// Should never be able to get here while in call()
|
||||
bassert (m_listener == 0);
|
||||
|
||||
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end (); ++iter)
|
||||
{
|
||||
Entry* entry = & (*iter);
|
||||
|
||||
if (entry->listener == listener)
|
||||
{
|
||||
m_list.erase (m_list.iterator_to (*entry));
|
||||
delete entry;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// Used for assertions.
|
||||
// The caller must synchronize.
|
||||
//
|
||||
bool ListenersBase::Group::contains (void* const listener) /*const*/
|
||||
{
|
||||
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end (); iter++)
|
||||
if (iter->listener == listener)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ListenersBase::Group::call (Call* const c, const timestamp_t timestamp)
|
||||
{
|
||||
bassert (!empty ());
|
||||
m_fifo.callp (new (m_fifo.getAllocator ()) GroupWork (this, c, timestamp));
|
||||
}
|
||||
|
||||
void ListenersBase::Group::queue (Call* const c, const timestamp_t timestamp)
|
||||
{
|
||||
bassert (!empty ());
|
||||
m_fifo.queuep (new (m_fifo.getAllocator ()) GroupWork (this, c, timestamp));
|
||||
}
|
||||
|
||||
void ListenersBase::Group::call1 (Call* const c,
|
||||
const timestamp_t timestamp,
|
||||
void* const listener)
|
||||
{
|
||||
m_fifo.callp (new (m_fifo.getAllocator ()) GroupWork1 (
|
||||
this, c, timestamp, listener));
|
||||
}
|
||||
|
||||
void ListenersBase::Group::queue1 (Call* const c,
|
||||
const timestamp_t timestamp,
|
||||
void* const listener)
|
||||
{
|
||||
m_fifo.queuep (new (m_fifo.getAllocator ()) GroupWork1 (
|
||||
this, c, timestamp, listener));
|
||||
}
|
||||
|
||||
// Queues a reference to the Call on the thread queue of each listener
|
||||
// that is currently in our list. The thread queue must be in the
|
||||
// stack's call chain, either directly from CallQueue::synchronize(),
|
||||
// or from Proxy::do_call() called from CallQueue::synchronize().
|
||||
//
|
||||
void ListenersBase::Group::do_call (Call* const c, const timestamp_t timestamp)
|
||||
{
|
||||
if (!empty ())
|
||||
{
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_mutex);
|
||||
|
||||
// Recursion not allowed.
|
||||
bassert (m_listener == 0);
|
||||
|
||||
// The body of the loop MUST NOT cause listeners to get called.
|
||||
// Therefore, we don't have to worry about listeners removing
|
||||
// themselves while iterating the list.
|
||||
//
|
||||
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end ();)
|
||||
{
|
||||
Entry* entry = & (*iter++);
|
||||
|
||||
// Since it is possible for a listener to be added after a
|
||||
// Call gets queued but before it executes, this prevents listeners
|
||||
// from seeing Calls created before they were added.
|
||||
//
|
||||
if (timestamp > entry->timestamp)
|
||||
{
|
||||
m_listener = entry->listener;
|
||||
|
||||
// The thread queue's synchronize() function MUST be in our call
|
||||
// stack to guarantee that these calls will not execute immediately.
|
||||
// They will be handled by the tail recusion unrolling in the
|
||||
// thread queue.
|
||||
bassert (m_fifo.isBeingSynchronized ());
|
||||
|
||||
m_fifo.callp (new (m_fifo.getAllocator ()) CallWork (c, m_listener));
|
||||
|
||||
m_listener = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// last listener was removed before we got here,
|
||||
// and the parent listener list may have been deleted.
|
||||
}
|
||||
}
|
||||
|
||||
void ListenersBase::Group::do_call1 (Call* const c, const timestamp_t timestamp,
|
||||
void* const listener)
|
||||
{
|
||||
if (!empty ())
|
||||
{
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_mutex);
|
||||
|
||||
// Recursion not allowed.
|
||||
bassert (m_listener == 0);
|
||||
|
||||
for (List <Entry>::iterator iter = m_list.begin (); iter != m_list.end ();)
|
||||
{
|
||||
Entry* entry = & (*iter++);
|
||||
|
||||
if (entry->listener == listener)
|
||||
{
|
||||
if (timestamp > entry->timestamp)
|
||||
{
|
||||
m_listener = entry->listener;
|
||||
|
||||
bassert (m_fifo.isBeingSynchronized ());
|
||||
|
||||
m_fifo.callp (new (m_fifo.getAllocator ()) CallWork (c, m_listener));
|
||||
|
||||
m_listener = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Listener was removed
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Proxy
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// CallQueue item for processing a an Entry for a Proxy.
|
||||
// This is used to avoid bind overhead.
|
||||
//
|
||||
class ListenersBase::Proxy::Work : public CallQueue::Work
|
||||
{
|
||||
public:
|
||||
inline Work (Proxy* proxy,
|
||||
Entry* const entry,
|
||||
const timestamp_t timestamp)
|
||||
: m_entry (entry)
|
||||
, m_timestamp (timestamp)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () ()
|
||||
{
|
||||
ListenersBase::Call* c = m_entry->call.exchange (0);
|
||||
|
||||
Group* group = m_entry->group;
|
||||
|
||||
if (!group->empty ())
|
||||
group->do_call (c, m_timestamp);
|
||||
|
||||
c->decReferenceCount ();
|
||||
}
|
||||
|
||||
private:
|
||||
Entry::Ptr m_entry;
|
||||
const timestamp_t m_timestamp;
|
||||
};
|
||||
|
||||
// Holds a Call, and gets put in the CallQueue in place of the Call.
|
||||
// The Call may be replaced if it hasn't been processed yet.
|
||||
// A Proxy exists for the lifetime of the Listeners.
|
||||
//
|
||||
ListenersBase::Proxy::Proxy (void const* const member, const size_t bytes)
|
||||
: m_bytes (bytes)
|
||||
{
|
||||
if (bytes > maxMemberBytes)
|
||||
Throw (Error ().fail (__FILE__, __LINE__, "the Proxy member is too large"));
|
||||
|
||||
memcpy (m_member, member, bytes);
|
||||
}
|
||||
|
||||
ListenersBase::Proxy::~Proxy ()
|
||||
{
|
||||
// If the proxy is getting destroyed it means:
|
||||
// - the listeners object is getting destroyed
|
||||
// - all listeners must have removed themselves
|
||||
// - all thread queues have been fully processed
|
||||
// Therefore, our entries should be gone.
|
||||
|
||||
// NO it is possible for an empty Group, for which
|
||||
// the parent listeners object has been destroyed,
|
||||
// to still exist in a thread queue!!!
|
||||
|
||||
// But all listeners should have removed themselves
|
||||
// so our list of groups should still be empty.
|
||||
bassert (m_entries.empty ());
|
||||
}
|
||||
|
||||
// Adds the group to the Proxy.
|
||||
// Caller must have the proxies mutex.
|
||||
// Caller is responsible for preventing duplicates.
|
||||
//
|
||||
void ListenersBase::Proxy::add (Group* group, AllocatorType& allocator)
|
||||
{
|
||||
Entry* entry (new (allocator) Entry (group));
|
||||
|
||||
// Manual addref and put raw pointer in list
|
||||
entry->incReferenceCount ();
|
||||
m_entries.push_back (*entry);
|
||||
}
|
||||
|
||||
// Removes the group from the Proxy.
|
||||
// Caller must have the proxies mutex.
|
||||
// Caller is responsible for making sure the group exists.
|
||||
void ListenersBase::Proxy::remove (Group* group)
|
||||
{
|
||||
for (Entries::iterator iter = m_entries.begin (); iter != m_entries.end ();)
|
||||
{
|
||||
Entry* entry = & (*iter++);
|
||||
|
||||
if (entry->group == group)
|
||||
{
|
||||
// remove from list and manual release
|
||||
m_entries.erase (m_entries.iterator_to (*entry));
|
||||
entry->decReferenceCount ();
|
||||
|
||||
// Entry might still be in the empty group's thread queue
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each group, updates the call.
|
||||
// Queues each group that isn't already queued.
|
||||
// Caller must acquire the group read lock.
|
||||
//
|
||||
void ListenersBase::Proxy::update (Call* const c, const timestamp_t timestamp)
|
||||
{
|
||||
// why would we even want to be called?
|
||||
bassert (!m_entries.empty ());
|
||||
|
||||
// With the read lock, this list can't change on us unless someone
|
||||
// adds a listener to a new thread queue in response to a call.
|
||||
for (Entries::iterator iter = m_entries.begin (); iter != m_entries.end ();)
|
||||
{
|
||||
Entry* entry = & (*iter++);
|
||||
|
||||
// Manually add a reference since we use a raw pointer
|
||||
c->incReferenceCount ();
|
||||
|
||||
// Atomically exchange the new call for the old one
|
||||
Call* old = entry->call.exchange (c);
|
||||
|
||||
// If no old call then they need to be queued
|
||||
if (!old)
|
||||
{
|
||||
CallQueue& callQueue = entry->group->getCallQueue ();
|
||||
callQueue.callp (new (callQueue.getAllocator ()) Work (this, entry, timestamp));
|
||||
}
|
||||
else
|
||||
{
|
||||
old->decReferenceCount ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ListenersBase::Proxy::match (void const* const member, const size_t bytes) const
|
||||
{
|
||||
return m_bytes == bytes && memcmp (member, m_member, bytes) == 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// ListenersBase
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ListenersBase::ListenersBase ()
|
||||
: m_timestamp (0)
|
||||
, m_allocator (AllocatorType::getInstance ())
|
||||
, m_callAllocator (CallAllocatorType::getInstance ())
|
||||
{
|
||||
}
|
||||
|
||||
ListenersBase::~ListenersBase ()
|
||||
{
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group* group = & (*iter++);
|
||||
|
||||
// If this goes off it means a Listener forgot to remove.
|
||||
bassert (group->empty ());
|
||||
|
||||
group->decReferenceCount ();
|
||||
}
|
||||
|
||||
// Proxies are never deleted until here.
|
||||
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
|
||||
delete & (*iter++);
|
||||
}
|
||||
|
||||
void ListenersBase::add_void (void* const listener, CallQueue& callQueue)
|
||||
{
|
||||
ReadWriteMutex::ScopedWriteLockType lock (m_groups_mutex);
|
||||
|
||||
#if BEAST_DEBUG
|
||||
|
||||
// Make sure the listener has not already been added
|
||||
// SHOULD USE const_iterator!
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group* group = & (*iter++);
|
||||
|
||||
// We can be in do_call() on another thread now, but it
|
||||
// doesn't modify the list, and we have the write lock.
|
||||
bassert (!group->contains (listener));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// See if we already have a Group for this thread queue.
|
||||
Group::Ptr group;
|
||||
|
||||
// SHOULD USE const_iterator
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group::Ptr cur = & (*iter++);
|
||||
|
||||
if (&cur->getCallQueue () == &callQueue)
|
||||
{
|
||||
group = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!group)
|
||||
{
|
||||
group = new (m_allocator) Group (callQueue);
|
||||
|
||||
// Add it to the list, and give it a manual ref
|
||||
// since the list currently uses raw pointers.
|
||||
group->incReferenceCount ();
|
||||
m_groups.push_back (*group);
|
||||
|
||||
// Tell existing proxies to add the group
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_proxies_mutex);
|
||||
|
||||
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
|
||||
(iter++)->add (group, *m_allocator);
|
||||
}
|
||||
|
||||
// Add the listener to the group with the current timestamp
|
||||
group->add (listener, m_timestamp, *m_allocator);
|
||||
|
||||
// Increment the timestamp within the mutex so
|
||||
// future calls will be newer than this listener.
|
||||
++m_timestamp;
|
||||
}
|
||||
|
||||
void ListenersBase::remove_void (void* const listener)
|
||||
{
|
||||
ReadWriteMutex::ScopedWriteLockType lock (m_groups_mutex);
|
||||
|
||||
// Make sure the listener exists
|
||||
#if BEAST_DEBUG
|
||||
{
|
||||
bool exists = false;
|
||||
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group* group = & (*iter++);
|
||||
|
||||
// this should never happen while we hold the mutex
|
||||
bassert (!group->empty ());
|
||||
|
||||
if (group->contains (listener))
|
||||
{
|
||||
bassert (!exists); // added twice?
|
||||
|
||||
exists = true;
|
||||
// keep going to make sure there are no empty groups
|
||||
}
|
||||
}
|
||||
|
||||
bassert (exists);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Find the group and remove
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group::Ptr group = & (*iter++);
|
||||
|
||||
// If the listener is in there, take it out.
|
||||
if (group->remove (listener))
|
||||
{
|
||||
// Are we the last listener?
|
||||
if (group->empty ())
|
||||
{
|
||||
// Tell proxies to remove the group
|
||||
{
|
||||
ReadWriteMutex::ScopedWriteLockType lock (m_proxies_mutex);
|
||||
|
||||
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
|
||||
{
|
||||
Proxy* proxy = & (*iter++);
|
||||
proxy->remove (group);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove it from the list and manually release
|
||||
// the reference since the list uses raw pointers.
|
||||
m_groups.erase (m_groups.iterator_to (*group.getObject ()));
|
||||
group->decReferenceCount ();
|
||||
|
||||
// It is still possible for the group to exist at this
|
||||
// point in a thread queue but it will get processed,
|
||||
// do nothing, and release its own final reference.
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenersBase::callp (Call::Ptr cp)
|
||||
{
|
||||
Call* c = cp;
|
||||
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
|
||||
|
||||
// can't be const iterator because queue() might cause called functors
|
||||
// to modify the list.
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
(iter++)->call (c, m_timestamp);
|
||||
}
|
||||
|
||||
void ListenersBase::queuep (Call::Ptr cp)
|
||||
{
|
||||
Call* c = cp;
|
||||
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
|
||||
|
||||
// can't be const iterator because queue() might cause called functors
|
||||
// to modify the list.
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
(iter++)->queue (c, m_timestamp);
|
||||
}
|
||||
|
||||
void ListenersBase::call1p_void (void* const listener, Call* c)
|
||||
{
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
|
||||
|
||||
// can't be const iterator because queue() might cause called functors
|
||||
// to modify the list.
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group* group = & (*iter++);
|
||||
|
||||
if (group->contains (listener))
|
||||
{
|
||||
group->call1 (c, m_timestamp, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListenersBase::queue1p_void (void* const listener, Call* c)
|
||||
{
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
|
||||
|
||||
// can't be const iterator because queue() might cause called functors
|
||||
// to modify the list.
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group* group = & (*iter++);
|
||||
|
||||
if (group->contains (listener))
|
||||
{
|
||||
group->queue1 (c, m_timestamp, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search for an existing Proxy that matches the pointer to
|
||||
// member and replace it's Call, or create a new Proxy for it.
|
||||
//
|
||||
void ListenersBase::updatep (void const* const member,
|
||||
const size_t bytes, Call::Ptr cp)
|
||||
{
|
||||
Call* c = cp;
|
||||
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_groups_mutex);
|
||||
|
||||
if (!m_groups.empty ())
|
||||
{
|
||||
Proxy* proxy;
|
||||
|
||||
{
|
||||
ReadWriteMutex::ScopedReadLockType lock (m_proxies_mutex);
|
||||
|
||||
// See if there's already a proxy
|
||||
proxy = find_proxy (member, bytes);
|
||||
}
|
||||
|
||||
// Possibly create one
|
||||
if (!proxy)
|
||||
{
|
||||
ReadWriteMutex::ScopedWriteLockType lock (m_proxies_mutex);
|
||||
|
||||
// Have to search for it again in case someone else added it
|
||||
proxy = find_proxy (member, bytes);
|
||||
|
||||
if (!proxy)
|
||||
{
|
||||
// Create a new empty proxy
|
||||
proxy = new (m_allocator) Proxy (member, bytes);
|
||||
|
||||
// Add all current groups to the Proxy.
|
||||
// We need the group read lock for this (caller provided).
|
||||
for (Groups::iterator iter = m_groups.begin (); iter != m_groups.end ();)
|
||||
{
|
||||
Group* group = & (*iter++);
|
||||
proxy->add (group, *m_allocator);
|
||||
}
|
||||
|
||||
// Add it to the list.
|
||||
m_proxies.push_front (*proxy);
|
||||
}
|
||||
}
|
||||
|
||||
// Requires the group read lock
|
||||
proxy->update (c, m_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for a proxy that matches the pointer to member.
|
||||
// Caller synchronizes.
|
||||
//
|
||||
ListenersBase::Proxy* ListenersBase::find_proxy (const void* member, size_t bytes)
|
||||
{
|
||||
for (Proxies::iterator iter = m_proxies.begin (); iter != m_proxies.end ();)
|
||||
{
|
||||
Proxy* proxy = & (*iter++);
|
||||
|
||||
if (proxy->match (member, bytes))
|
||||
return proxy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,797 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_LISTENERS_BEASTHEADER
|
||||
#define BEAST_LISTENERS_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A group of concurrent Listeners.
|
||||
|
||||
A Listener is an object of class type which inherits from a defined
|
||||
interface, and registers on a provided instance of Listeners to receive
|
||||
asynchronous notifications of changes to concurrent states. Another way of
|
||||
defining Listeners, is that it is similar to a Juce ListenerList but with
|
||||
the provision that the Listener registers with the CallQueue upon which the
|
||||
notification should be made.
|
||||
|
||||
Listeners makes extensive use of CallQueue for providing the notifications,
|
||||
and provides a higher level facility for implementing the concurrent
|
||||
synchronization strategy outlined in CallQueue. Therefore, the same notes
|
||||
which apply to functors in CallQueue also apply to Listener member
|
||||
invocations. Their execution time should be brief, limited in scope to
|
||||
updating the recipient's view of a shared state, and use reference counting
|
||||
for parameters of class type.
|
||||
|
||||
To use this system, first declare your Listener interface:
|
||||
|
||||
@code
|
||||
|
||||
struct Listener
|
||||
{
|
||||
// Sent on every output block
|
||||
virtual void onOutputLevelChanged (const float outputLevel) { }
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
Now set up the place where you want to send the notifications. In this
|
||||
example, we will set up the AudioIODeviceCallback to notify anyone who is
|
||||
interested about changes in the current audio output level. We will use
|
||||
this to implement a VU meter:
|
||||
|
||||
@code
|
||||
|
||||
Listeners <Listener> listeners;
|
||||
|
||||
// (Process audio data)
|
||||
|
||||
// Calculate output level
|
||||
float outputLevel = calcOutputLevel ();
|
||||
|
||||
// Notify listeners
|
||||
listeners.call (&Listener::onOutputLevelChanged, outputLevel);
|
||||
|
||||
@endcode
|
||||
|
||||
To receive notifications, derive from Listener and then add yourself to the
|
||||
Listeners object using the desired CallQueue.
|
||||
|
||||
@code
|
||||
|
||||
// We want notifications on the message thread
|
||||
GuiCallQueue fifo;
|
||||
|
||||
struct VUMeter : public Listener, public Component
|
||||
{
|
||||
VUMeter () : m_outputLevel (0)
|
||||
{
|
||||
listeners.add (this, fifo);
|
||||
}
|
||||
|
||||
~VUMeter ()
|
||||
{
|
||||
listeners.remove (this);
|
||||
}
|
||||
|
||||
void onOutputLevelChanged (float outputLevel)
|
||||
{
|
||||
// Update our copy of the output level shared state.
|
||||
m_outputLevel = outputLevel;
|
||||
|
||||
// Now trigger a redraw of the control.
|
||||
repaint ();
|
||||
}
|
||||
|
||||
float m_outputLevel;
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
In this example, the VUMeter constructs with the output level set to zero,
|
||||
and must wait for a notification before it shows up to date data. For a
|
||||
simple VU meter, this is likely not a problem. But if the shared state
|
||||
contains complex information, such as dynamically allocated objects with
|
||||
rich data, then we need a more solid system.
|
||||
|
||||
We will add some classes to create a complete robust example of the use of
|
||||
Listeners to synchronize shared state:
|
||||
|
||||
@code
|
||||
|
||||
// Handles audio device output.
|
||||
class AudioDeviceOutput : public AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
struct Listener
|
||||
{
|
||||
// Sent on every output block.
|
||||
virtual void onOutputLevelChanged (float outputLevel) { }
|
||||
};
|
||||
|
||||
AudioDeviceOutput () : AudioDeviceOutput ("Audio CallQueue")
|
||||
{
|
||||
}
|
||||
|
||||
~AudioDeviceOutput ()
|
||||
{
|
||||
m_fifo.close ();
|
||||
}
|
||||
|
||||
void addListener (Listener* listener, CallQueue& callQueue)
|
||||
{
|
||||
// Acquire read access to the shared state.
|
||||
SharedData <State>::ReadAccess state (m_state);
|
||||
|
||||
// Add the listener.
|
||||
m_listeners.add (listener, callQueue);
|
||||
|
||||
// Queue an update for the listener to receive the initial state.
|
||||
m_listeners.queue1 (listener,
|
||||
&Listener::onOutputLevelChanged,
|
||||
state->outputLevel);
|
||||
}
|
||||
|
||||
void removeListener (Listener* listener)
|
||||
{
|
||||
m_listeners.remove (listener);
|
||||
}
|
||||
|
||||
protected:
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
// Synchronize our call queue. Not needed for this example but
|
||||
// included here as a best-practice for audio device I/O callbacks.
|
||||
m_fifo.synchronize ();
|
||||
|
||||
// (Process audio data)
|
||||
|
||||
// Calculate output level.
|
||||
float newOutputLevel = calcOutputLevel ();
|
||||
|
||||
// Update shared state.
|
||||
{
|
||||
SharedData <State>::WriteAccess state (m_state);
|
||||
|
||||
m_state->outputLevel = newOutputLevel;
|
||||
}
|
||||
|
||||
// Notify listeners.
|
||||
listeners.call (&Listener::onOutputLevelChanged, newOutputLevel);
|
||||
}
|
||||
|
||||
private:
|
||||
struct State
|
||||
{
|
||||
State () : outputLevel (0) { }
|
||||
|
||||
float outputLevel;
|
||||
};
|
||||
|
||||
SharedData <State> m_state;
|
||||
|
||||
ManualCallQueue m_fifo;
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
Although the rigor demonstrated in the example above is not strictly
|
||||
required when the shared state consists only of a single float, it
|
||||
becomes necessary when there are dynamically allocated objects with complex
|
||||
interactions in the shared state.
|
||||
|
||||
@see CallQueue
|
||||
|
||||
@class Listeners
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API ListenersBase
|
||||
{
|
||||
public:
|
||||
struct ListenersStructureTag { };
|
||||
|
||||
typedef GlobalFifoFreeStore <ListenersStructureTag> AllocatorType;
|
||||
|
||||
typedef GlobalFifoFreeStore <ListenersBase> CallAllocatorType;
|
||||
|
||||
class Call : public SharedObject,
|
||||
public AllocatedBy <CallAllocatorType>
|
||||
{
|
||||
public:
|
||||
typedef SharedObjectPtr <Call> Ptr;
|
||||
virtual void operator () (void* const listener) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
typedef unsigned long timestamp_t;
|
||||
|
||||
class Group;
|
||||
typedef List <Group> Groups;
|
||||
|
||||
class Proxy;
|
||||
typedef List <Proxy> Proxies;
|
||||
|
||||
class CallWork;
|
||||
class GroupWork;
|
||||
class GroupWork1;
|
||||
|
||||
// Maintains a list of listeners registered on the same CallQueue
|
||||
//
|
||||
class Group : public Groups::Node,
|
||||
public SharedObject,
|
||||
public AllocatedBy <AllocatorType>
|
||||
{
|
||||
public:
|
||||
typedef SharedObjectPtr <Group> Ptr;
|
||||
|
||||
explicit Group (CallQueue& callQueue);
|
||||
~Group ();
|
||||
void add (void* listener, const timestamp_t timestamp,
|
||||
AllocatorType& allocator);
|
||||
bool remove (void* listener);
|
||||
bool contains (void* const listener);
|
||||
void call (Call* const c, const timestamp_t timestamp);
|
||||
void queue (Call* const c, const timestamp_t timestamp);
|
||||
void call1 (Call* const c, const timestamp_t timestamp,
|
||||
void* const listener);
|
||||
void queue1 (Call* const c, const timestamp_t timestamp,
|
||||
void* const listener);
|
||||
void do_call (Call* const c, const timestamp_t timestamp);
|
||||
void do_call1 (Call* const c, const timestamp_t timestamp,
|
||||
void* const listener);
|
||||
|
||||
bool empty () const
|
||||
{
|
||||
return m_list.empty ();
|
||||
}
|
||||
CallQueue& getCallQueue () const
|
||||
{
|
||||
return m_fifo;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry;
|
||||
|
||||
CallQueue& m_fifo;
|
||||
List <Entry> m_list;
|
||||
void* m_listener;
|
||||
CacheLine::Aligned <ReadWriteMutex> m_mutex;
|
||||
};
|
||||
|
||||
// A Proxy is keyed to a unique pointer-to-member of a
|
||||
// ListenerClass and is used to consolidate multiple unprocessed
|
||||
// Calls into a single call to prevent excess messaging. It is up
|
||||
// to the user of the class to decide when this behavior is appropriate.
|
||||
//
|
||||
class Proxy : public Proxies::Node,
|
||||
public AllocatedBy <AllocatorType>
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
maxMemberBytes = 16
|
||||
};
|
||||
|
||||
Proxy (void const* const member, const size_t bytes);
|
||||
~Proxy ();
|
||||
|
||||
void add (Group* group, AllocatorType& allocator);
|
||||
void remove (Group* group);
|
||||
void update (Call* const c, const timestamp_t timestamp);
|
||||
|
||||
bool match (void const* const member, const size_t bytes) const;
|
||||
|
||||
private:
|
||||
class Work;
|
||||
struct Entry;
|
||||
typedef List <Entry> Entries;
|
||||
char m_member [maxMemberBytes];
|
||||
const size_t m_bytes;
|
||||
Entries m_entries;
|
||||
};
|
||||
|
||||
protected:
|
||||
ListenersBase ();
|
||||
~ListenersBase ();
|
||||
|
||||
inline CallAllocatorType& getCallAllocator ()
|
||||
{
|
||||
return *m_callAllocator;
|
||||
}
|
||||
|
||||
void add_void (void* const listener, CallQueue& callQueue);
|
||||
void remove_void (void* const listener);
|
||||
|
||||
void callp (Call::Ptr c);
|
||||
void queuep (Call::Ptr c);
|
||||
void call1p_void (void* const listener, Call* c);
|
||||
void queue1p_void (void* const listener, Call* c);
|
||||
void updatep (void const* const member,
|
||||
const size_t bytes, Call::Ptr cp);
|
||||
|
||||
private:
|
||||
Proxy* find_proxy (const void* member, size_t bytes);
|
||||
|
||||
private:
|
||||
Groups m_groups;
|
||||
Proxies m_proxies;
|
||||
timestamp_t m_timestamp;
|
||||
CacheLine::Aligned <ReadWriteMutex> m_groups_mutex;
|
||||
CacheLine::Aligned <ReadWriteMutex> m_proxies_mutex;
|
||||
AllocatorType::Ptr m_allocator;
|
||||
CallAllocatorType::Ptr m_callAllocator;
|
||||
};
|
||||
|
||||
/*============================================================================*/
|
||||
|
||||
template <class ListenerClass>
|
||||
class Listeners : public ListenersBase
|
||||
{
|
||||
private:
|
||||
template <class Functor>
|
||||
class CallType : public Call
|
||||
{
|
||||
public:
|
||||
CallType (Functor f) : m_f (f)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () (void* const listener)
|
||||
{
|
||||
ListenerClass* object = static_cast <ListenerClass*> (listener);
|
||||
m_f.operator () (object);
|
||||
}
|
||||
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
inline void callf (Functor f)
|
||||
{
|
||||
callp (new (getCallAllocator ()) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
inline void queuef (Functor f)
|
||||
{
|
||||
queuep (new (getCallAllocator ()) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
inline void call1p (ListenerClass* const listener, Call::Ptr c)
|
||||
{
|
||||
call1p_void (listener, c);
|
||||
}
|
||||
|
||||
inline void queue1p (ListenerClass* const listener, Call::Ptr c)
|
||||
{
|
||||
queue1p_void (listener, c);
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
inline void call1f (ListenerClass* const listener, Functor f)
|
||||
{
|
||||
call1p (listener, new (getCallAllocator ()) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
template <class Functor>
|
||||
inline void queue1f (ListenerClass* const listener, Functor f)
|
||||
{
|
||||
queue1p (listener, new (getCallAllocator ()) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
template <class Member, class Functor>
|
||||
inline void updatef (Member member, Functor f)
|
||||
{
|
||||
updatep (reinterpret_cast <void*> (&member), sizeof (Member),
|
||||
new (getCallAllocator ()) CallType <Functor> (f));
|
||||
}
|
||||
|
||||
public:
|
||||
/** Add a listener.
|
||||
|
||||
The specified listener is associated with the specified CallQueue and
|
||||
added to the list.
|
||||
|
||||
Invariants:
|
||||
|
||||
- All other members of Listeners are blocked during add().
|
||||
|
||||
- The listener is guaranteed to receive every subsequent call.
|
||||
|
||||
- The listener must not already exist in the list.
|
||||
|
||||
- Safe to call from any thread.
|
||||
|
||||
@param listener The listener to add.
|
||||
|
||||
@param callQueue The CallQueue to associate with the listener.
|
||||
*/
|
||||
void add (ListenerClass* const listener, CallQueue& callQueue)
|
||||
{
|
||||
add_void (listener, callQueue);
|
||||
}
|
||||
|
||||
/** Remove a listener.
|
||||
|
||||
The specified listener, which must have been previously added, is removed
|
||||
from the list. A listener always needs to remove itself before the
|
||||
associated CallQueue is closed.
|
||||
|
||||
Invariants:
|
||||
|
||||
- All other members of Listeners are blocked during remove().
|
||||
|
||||
- The listener is guaranteed not to receive calls after remove() returns.
|
||||
|
||||
- Safe to call from any thread.
|
||||
|
||||
@param listener The listener to remove.
|
||||
*/
|
||||
void remove (ListenerClass* const listener)
|
||||
{
|
||||
remove_void (listener);
|
||||
}
|
||||
|
||||
/** Call a member function on every added listener, on its associated
|
||||
CallQueue.
|
||||
|
||||
A listener's CallQueue will be synchronized if this function is called
|
||||
from it's associated thread.
|
||||
|
||||
Invariants:
|
||||
|
||||
- A listener that later removes itself afterwards may not get called.
|
||||
|
||||
- Calls from the same thread always execute in order.
|
||||
|
||||
- A listener can remove itself even if it has a pending call.
|
||||
|
||||
@param mf The member function to call. This may be followed by up to 8
|
||||
arguments.
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Mf>
|
||||
inline void call (Mf mf)
|
||||
{ callf (functional::bind (mf, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Mf, class T1>
|
||||
void call (Mf mf, T1 t1)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Mf, class T1, class T2>
|
||||
void call (Mf mf, T1 t1, T2 t2)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Mf, class T1, class T2, class T3>
|
||||
void call (Mf mf, T1 t1, T2 t2, T3 t3)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Mf, class T1, class T2, class T3, class T4>
|
||||
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5>
|
||||
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void call (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ callf (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/** Queue a member function on every added listener, without synchronizing.
|
||||
|
||||
Operates like call(), but no CallQueue synchronization takes place. This
|
||||
can be necessary when the call to queue() is made inside a held lock.
|
||||
|
||||
@param mf The member function to call. This may be followed by up to 8
|
||||
arguments.
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Mf>
|
||||
inline void queue (Mf mf)
|
||||
{ queuef (functional::bind (mf, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Mf, class T1>
|
||||
void queue (Mf mf, T1 t1)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Mf, class T1, class T2>
|
||||
void queue (Mf mf, T1 t1, T2 t2)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Mf, class T1, class T2, class T3>
|
||||
void queue (Mf mf, T1 t1, T2 t2, T3 t3)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Mf, class T1, class T2, class T3, class T4>
|
||||
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5>
|
||||
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void queue (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ queuef (functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/** Call a member function on every added listener, replacing pending
|
||||
calls to the same member.
|
||||
|
||||
This operates like call(), except that if there are pending unprocessed
|
||||
calls to the same member function,they will be replaced, with the previous
|
||||
parameters destroyed normally. This functionality is useful for
|
||||
high frequency notifications of non critical data, where the recipient
|
||||
may not catch up often enough. For example, the output level of the
|
||||
AudioIODeviceCallback in the example is a candidate for the use of
|
||||
update().
|
||||
|
||||
@param mf The member function to call. This may be followed by up to 8
|
||||
arguments.
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Mf>
|
||||
inline void update (Mf mf)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Mf, class T1>
|
||||
void update (Mf mf, T1 t1)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Mf, class T1, class T2>
|
||||
void update (Mf mf, T1 t1, T2 t2)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Mf, class T1, class T2, class T3>
|
||||
void update (Mf mf, T1 t1, T2 t2, T3 t3)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Mf, class T1, class T2, class T3, class T4>
|
||||
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5>
|
||||
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void update (Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ updatef (mf, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/** Call a member function on a specific listener.
|
||||
|
||||
Like call(), except that one listener is targeted only. This is useful when
|
||||
builing complex behaviors during the addition of a listener, such as
|
||||
providing an initial state.
|
||||
|
||||
@param listener The listener to call.
|
||||
|
||||
@param mf The member function to call. This may be followed by up
|
||||
to 8 arguments.
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Mf>
|
||||
inline void call1 (ListenerClass* const listener, Mf mf)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Mf, class T1>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Mf, class T1, class T2>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Mf, class T1, class T2, class T3>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Mf, class T1, class T2, class T3, class T4>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void call1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ call1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/** Queue a member function on a specific listener.
|
||||
|
||||
Like call1(), except that no CallQueue synchronization takes place.
|
||||
|
||||
@param listener The listener to call.
|
||||
|
||||
@param mf The member function to call. This may be followed by up
|
||||
to 8 arguments.
|
||||
*/
|
||||
/** @{ */
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Mf>
|
||||
inline void queue1 (ListenerClass* const listener, Mf mf)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Mf, class T1>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Mf, class T1, class T2>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Mf, class T1, class T2, class T3>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Mf, class T1, class T2, class T3, class T4>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Mf, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void queue1 (ListenerClass* const listener, Mf mf, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ queue1f (listener, functional::bind (mf, placeholders::_1, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
};
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
/*============================================================================*/
|
||||
/*
|
||||
VFLib: https://github.com/vinniefalco/VFLib
|
||||
|
||||
Copyright (C) 2008 by Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
This library contains portions of other open source products covered by
|
||||
separate licenses. Please see the corresponding source files for specific
|
||||
terms.
|
||||
|
||||
VFLib is provided under the terms of The MIT License (MIT):
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
/*============================================================================*/
|
||||
|
||||
ManualCallQueue::ManualCallQueue (String name)
|
||||
: CallQueue (name)
|
||||
{
|
||||
}
|
||||
|
||||
void ManualCallQueue::close ()
|
||||
{
|
||||
CallQueue::close ();
|
||||
}
|
||||
|
||||
bool ManualCallQueue::synchronize ()
|
||||
{
|
||||
return CallQueue::synchronize ();
|
||||
}
|
||||
|
||||
void ManualCallQueue::signal ()
|
||||
{
|
||||
}
|
||||
|
||||
void ManualCallQueue::reset ()
|
||||
{
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*============================================================================*/
|
||||
/*
|
||||
VFLib: https://github.com/vinniefalco/VFLib
|
||||
|
||||
Copyright (C) 2008 by Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
This library contains portions of other open source products covered by
|
||||
separate licenses. Please see the corresponding source files for specific
|
||||
terms.
|
||||
|
||||
VFLib is provided under the terms of The MIT License (MIT):
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
/*============================================================================*/
|
||||
|
||||
#ifndef VF_MANUALCALLQUEUE_VFHEADER
|
||||
#define VF_MANUALCALLQUEUE_VFHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A CallQueue that requires periodic manual synchronization.
|
||||
|
||||
To use this, declare an instance and then place calls into it as usual.
|
||||
Every so often, you must call synchronize() from the thread you want to
|
||||
associate with the queue. Typically this is done within an
|
||||
AudioIODeviceCallback:
|
||||
|
||||
@code
|
||||
|
||||
class AudioIODeviceCallbackWithCallQueue
|
||||
: public AudioIODeviceCallback
|
||||
, public CallQueue
|
||||
{
|
||||
public:
|
||||
AudioIODeviceCallbackWithCallQueue () : m_fifo ("Audio CallQueue")
|
||||
{
|
||||
}
|
||||
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
CallQueue::synchronize ();
|
||||
|
||||
// do audio i/o
|
||||
}
|
||||
|
||||
void signal () { } // No action required
|
||||
void reset () { } // No action required
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
The close() function is provided for diagnostics. Call it as early as
|
||||
possible based on the exit or shutdown logic of your application. If calls
|
||||
are put into the queue after it is closed, it will generate an exception so
|
||||
you can track it down.
|
||||
|
||||
@see CallQueue
|
||||
|
||||
@ingroup vf_concurrent
|
||||
*/
|
||||
class ManualCallQueue : public CallQueue
|
||||
{
|
||||
public:
|
||||
/** Create a ManualCallQueue.
|
||||
|
||||
@param name A string used to help identify the associated
|
||||
thread for debugging.
|
||||
*/
|
||||
explicit ManualCallQueue (String name);
|
||||
|
||||
/** Close the queue. If calls are placed into a closed queue, an exception
|
||||
is thrown.
|
||||
*/
|
||||
void close ();
|
||||
|
||||
/** Synchronize the queue by calling all pending functors.
|
||||
|
||||
@return `true` if any functors were called.
|
||||
*/
|
||||
bool synchronize ();
|
||||
|
||||
private:
|
||||
void signal ();
|
||||
void reset ();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ParallelFor::ParallelFor (ThreadGroup& pool)
|
||||
: m_pool (pool)
|
||||
, m_finishedEvent (false) // auto-reset
|
||||
{
|
||||
}
|
||||
|
||||
int ParallelFor::getNumberOfThreads () const
|
||||
{
|
||||
return m_pool.getNumberOfThreads ();
|
||||
}
|
||||
|
||||
void ParallelFor::doLoop (int numberOfIterations, Iteration& iteration)
|
||||
{
|
||||
if (numberOfIterations > 1)
|
||||
{
|
||||
int const numberOfThreads = m_pool.getNumberOfThreads ();
|
||||
|
||||
// The largest number of pool threads we need is one less than the number
|
||||
// of iterations, because we also run the loop body on the caller's thread.
|
||||
//
|
||||
int const maxThreads = numberOfIterations - 1;
|
||||
|
||||
// Calculate the number of parallel instances as the smaller of the number
|
||||
// of threads available (including the caller's) and the number of iterations.
|
||||
//
|
||||
int const numberOfParallelInstances = std::min (
|
||||
numberOfThreads + 1, numberOfIterations);
|
||||
|
||||
LoopState* loopState (new (m_pool.getAllocator ()) LoopState (
|
||||
iteration, m_finishedEvent, numberOfIterations, numberOfParallelInstances));
|
||||
|
||||
m_pool.call (maxThreads, &LoopState::forLoopBody, loopState);
|
||||
|
||||
// Also use the caller's thread to run the loop body.
|
||||
loopState->forLoopBody ();
|
||||
|
||||
m_finishedEvent.wait ();
|
||||
}
|
||||
else if (numberOfIterations == 1)
|
||||
{
|
||||
// Just one iteration, so do it.
|
||||
iteration (0);
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_PARALLELFOR_BEASTHEADER
|
||||
#define BEAST_PARALLELFOR_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Parallel for loop.
|
||||
|
||||
This uses a ThreadGroup to iterate through a for loop in parallel. The
|
||||
following two pieces of code perform identical operations:
|
||||
|
||||
@code
|
||||
|
||||
extern void function (int loopIndex);
|
||||
|
||||
// Serial computation
|
||||
//
|
||||
for (int i = 0; i < numberOfIterations; ++i)
|
||||
function (i);
|
||||
|
||||
// Parallel computation
|
||||
//
|
||||
ParallelFor().loop (numberOfIterations, &function);
|
||||
|
||||
@endcode
|
||||
|
||||
`function` is a caller provided functor. Convenience functions are provided
|
||||
for automatic binding to member or non member functions with up to 8
|
||||
arguments (not including the loop index).
|
||||
|
||||
@note The last argument to function () is always the loop index.
|
||||
|
||||
@see ThreadGroup
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API ParallelFor : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Create a parallel for loop.
|
||||
|
||||
It is best to keep this object around instead of creating and destroying
|
||||
it every time you need to run a loop.
|
||||
|
||||
@param pool The ThreadGroup to use. If this is omitted then a singleton
|
||||
ThreadGroup is used which contains one thread per CPU.
|
||||
*/
|
||||
explicit ParallelFor (ThreadGroup& pool = *GlobalThreadGroup::getInstance ());
|
||||
|
||||
/** Determine the number of threads in the group.
|
||||
|
||||
@return The number of threads in the group.
|
||||
*/
|
||||
int getNumberOfThreads () const;
|
||||
|
||||
/** Execute parallel for loop.
|
||||
|
||||
Functor is called once for each value in the range
|
||||
[0, numberOfIterations), using the ThreadGroup.
|
||||
|
||||
@param numberOfIterations The number of times to loop.
|
||||
|
||||
@param f The functor to call for each loop index.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Functor>
|
||||
void loopf (int numberOfIterations, Functor const& f)
|
||||
{
|
||||
IterationType <Functor> iteration (f);
|
||||
|
||||
doLoop (numberOfIterations, iteration);
|
||||
}
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Fn>
|
||||
void loop (int n, Fn f)
|
||||
{ loopf (n, functional::bind (f, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Fn, class T1>
|
||||
void loop (int n, Fn f, T1 t1)
|
||||
{ loopf (n, functional::bind (f, t1, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Fn, class T1, class T2>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2)
|
||||
{ loopf (n, functional::bind (f, t1, t2, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Fn, class T1, class T2, class T3>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2, T3 t3)
|
||||
{ loopf (n, functional::bind (f, t1, t2, t3, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Fn, class T1, class T2, class T3, class T4>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ loopf (n, functional::bind (f, t1, t2, t3, t4, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ loopf (n, functional::bind (f, t1, t2, t3, t4, t5, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ loopf (n, functional::bind (f, t1, t2, t3, t4, t5, t6, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ loopf (n, functional::bind (f, t1, t2, t3, t4, t5, t6, t7, placeholders::_1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void loop (int n, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ loopf (n, functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8, placeholders::_1)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
class Iteration
|
||||
{
|
||||
public:
|
||||
virtual ~Iteration () { }
|
||||
virtual void operator () (int loopIndex) = 0;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
class IterationType : public Iteration, public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit IterationType (Functor const& f) : m_f (f)
|
||||
{
|
||||
}
|
||||
|
||||
void operator () (int loopIndex)
|
||||
{
|
||||
m_f (loopIndex);
|
||||
}
|
||||
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
private:
|
||||
class LoopState
|
||||
: public AllocatedBy <ThreadGroup::AllocatorType>
|
||||
, public Uncopyable
|
||||
{
|
||||
private:
|
||||
Iteration& m_iteration;
|
||||
WaitableEvent& m_finishedEvent;
|
||||
int const m_numberOfIterations;
|
||||
Atomic <int> m_loopIndex;
|
||||
Atomic <int> m_iterationsRemaining;
|
||||
Atomic <int> m_numberOfParallelInstances;
|
||||
|
||||
public:
|
||||
LoopState (Iteration& iteration,
|
||||
WaitableEvent& finishedEvent,
|
||||
int numberOfIterations,
|
||||
int numberOfParallelInstances)
|
||||
: m_iteration (iteration)
|
||||
, m_finishedEvent (finishedEvent)
|
||||
, m_numberOfIterations (numberOfIterations)
|
||||
, m_loopIndex (-1)
|
||||
, m_iterationsRemaining (numberOfIterations)
|
||||
, m_numberOfParallelInstances (numberOfParallelInstances)
|
||||
{
|
||||
}
|
||||
|
||||
~LoopState ()
|
||||
{
|
||||
}
|
||||
|
||||
void forLoopBody ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// Request a loop index to process.
|
||||
int const loopIndex = ++m_loopIndex;
|
||||
|
||||
// Is it in range?
|
||||
if (loopIndex < m_numberOfIterations)
|
||||
{
|
||||
// Yes, so process it.
|
||||
m_iteration (loopIndex);
|
||||
|
||||
// Was this the last work item to complete?
|
||||
if (--m_iterationsRemaining == 0)
|
||||
{
|
||||
// Yes, signal.
|
||||
m_finishedEvent.signal ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Out of range, all work is complete or assigned.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
release ();
|
||||
}
|
||||
|
||||
void release ()
|
||||
{
|
||||
if (--m_numberOfParallelInstances == 0)
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
void doLoop (int numberOfIterations, Iteration& iteration);
|
||||
|
||||
private:
|
||||
ThreadGroup& m_pool;
|
||||
WaitableEvent m_finishedEvent;
|
||||
Atomic <int> m_currentIndex;
|
||||
Atomic <int> m_numberOfInstances;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,98 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ReadWriteMutex::ReadWriteMutex () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
ReadWriteMutex::~ReadWriteMutex () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void ReadWriteMutex::enterRead () const noexcept
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// attempt the lock optimistically
|
||||
// THIS IS NOT CACHE-FRIENDLY!
|
||||
m_readers->addref ();
|
||||
|
||||
// is there a writer?
|
||||
// THIS IS NOT CACHE-FRIENDLY!
|
||||
if (m_writes->isSignaled ())
|
||||
{
|
||||
// a writer exists, give up the read lock
|
||||
m_readers->release ();
|
||||
|
||||
// block until the writer is done
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
}
|
||||
|
||||
// now try the loop again
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::exitRead () const noexcept
|
||||
{
|
||||
m_readers->release ();
|
||||
}
|
||||
|
||||
void ReadWriteMutex::enterWrite () const noexcept
|
||||
{
|
||||
// Optimistically acquire the write lock.
|
||||
m_writes->addref ();
|
||||
|
||||
// Go for the mutex.
|
||||
// Another writer might block us here.
|
||||
m_mutex.enter ();
|
||||
|
||||
// Only one competing writer will get here,
|
||||
// but we don't know who, so we have to drain
|
||||
// readers no matter what. New readers will be
|
||||
// blocked by the mutex.
|
||||
//
|
||||
if (m_readers->isSignaled ())
|
||||
{
|
||||
SpinDelay delay;
|
||||
|
||||
do
|
||||
{
|
||||
delay.pause ();
|
||||
}
|
||||
while (m_readers->isSignaled ());
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::exitWrite () const noexcept
|
||||
{
|
||||
// Releasing the mutex first and then decrementing the
|
||||
// writer count allows another waiting writer to atomically
|
||||
// acquire the lock, thus starving readers. This fulfills
|
||||
// the write-preferencing requirement.
|
||||
|
||||
m_mutex.exit ();
|
||||
|
||||
m_writes->release ();
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_READWRITEMUTEX_BEASTHEADER
|
||||
#define BEAST_READWRITEMUTEX_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Multiple consumer, single producer (MCSP) synchronization.
|
||||
|
||||
This is an optimized lock for the multiple reader, single writer
|
||||
scenario. It provides only a subset of features of the more general
|
||||
traditional read/write lock. Specifically, these rules apply:
|
||||
|
||||
- A caller cannot hold a read lock while acquiring a write lock.
|
||||
|
||||
- Write locks are only recursive with respect to write locks.
|
||||
|
||||
- Read locks are only recursive with respect to read locks.
|
||||
|
||||
- A write lock cannot be downgraded.
|
||||
|
||||
- Writes are preferenced over reads.
|
||||
|
||||
For real-time applications, these restrictions are often not an issue.
|
||||
|
||||
The implementation is wait-free in the fast path: acquiring read access
|
||||
for a lock without contention - just one interlocked increment!
|
||||
|
||||
@class ReadWriteMutex
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Scoped read lock for ReadWriteMutex.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class LockType>
|
||||
struct GenericScopedReadLock : public Uncopyable
|
||||
{
|
||||
inline explicit GenericScopedReadLock (LockType const& lock) noexcept
|
||||
:
|
||||
m_lock (lock)
|
||||
{
|
||||
m_lock.enterRead ();
|
||||
}
|
||||
|
||||
inline ~GenericScopedReadLock () noexcept
|
||||
{
|
||||
m_lock.exitRead ();
|
||||
}
|
||||
|
||||
private:
|
||||
LockType const& m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Scoped write lock for ReadWriteMutex.
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
template <class LockType>
|
||||
struct GenericScopedWriteLock : public Uncopyable
|
||||
{
|
||||
inline explicit GenericScopedWriteLock (LockType const& lock) noexcept
|
||||
:
|
||||
m_lock (lock)
|
||||
{
|
||||
m_lock.enterWrite ();
|
||||
}
|
||||
|
||||
inline ~GenericScopedWriteLock () noexcept
|
||||
{
|
||||
m_lock.exitWrite ();
|
||||
}
|
||||
|
||||
private:
|
||||
LockType const& m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class BEAST_API ReadWriteMutex
|
||||
{
|
||||
public:
|
||||
/** Provides the type of scoped read lock to use with a ReadWriteMutex. */
|
||||
typedef GenericScopedReadLock <ReadWriteMutex> ScopedReadLockType;
|
||||
|
||||
/** Provides the type of scoped write lock to use with a ReadWriteMutex. */
|
||||
typedef GenericScopedWriteLock <ReadWriteMutex> ScopedWriteLockType;
|
||||
|
||||
/** Create a ReadWriteMutex */
|
||||
ReadWriteMutex () noexcept;
|
||||
|
||||
/** Destroy a ReadWriteMutex
|
||||
|
||||
If the object is destroyed while a lock is held, the result is
|
||||
undefined behavior.
|
||||
*/
|
||||
~ReadWriteMutex () noexcept;
|
||||
|
||||
/** Acquire a read lock.
|
||||
|
||||
This is recursive with respect to other read locks. Calling this while
|
||||
holding a write lock is undefined.
|
||||
*/
|
||||
void enterRead () const noexcept;
|
||||
|
||||
/** Release a previously acquired read lock */
|
||||
void exitRead () const noexcept;
|
||||
|
||||
/** Acquire a write lock.
|
||||
|
||||
This is recursive with respect to other write locks. Calling this while
|
||||
holding a read lock is undefined.
|
||||
*/
|
||||
void enterWrite () const noexcept;
|
||||
|
||||
/** Release a previously acquired write lock */
|
||||
void exitWrite () const noexcept;
|
||||
|
||||
private:
|
||||
CriticalSection m_mutex;
|
||||
|
||||
mutable CacheLine::Padded <AtomicCounter> m_writes;
|
||||
mutable CacheLine::Padded <AtomicCounter> m_readers;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,125 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Semaphore::Semaphore (int initialCount)
|
||||
: m_counter (initialCount)
|
||||
{
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore ()
|
||||
{
|
||||
// Can't delete the semaphore while threads are waiting on it!!
|
||||
bassert (m_waitingThreads.pop_front () == nullptr);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
WaitingThread* waitingThread = m_deleteList.pop_front ();
|
||||
|
||||
if (waitingThread != nullptr)
|
||||
delete waitingThread;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Semaphore::signal (int amount)
|
||||
{
|
||||
bassert (amount > 0);
|
||||
|
||||
while (amount--)
|
||||
{
|
||||
// Make counter and list operations atomic.
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
if (++m_counter <= 0)
|
||||
{
|
||||
WaitingThread* waitingThread = m_waitingThreads.pop_front ();
|
||||
|
||||
bassert (waitingThread != nullptr);
|
||||
|
||||
waitingThread->signal ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::wait (int timeOutMilliseconds)
|
||||
{
|
||||
bool signaled = true;
|
||||
|
||||
// Always prepare the WaitingThread object first, either
|
||||
// from the delete list or through a new allocation.
|
||||
//
|
||||
WaitingThread* waitingThread = m_deleteList.pop_front ();
|
||||
|
||||
if (waitingThread == nullptr)
|
||||
waitingThread = new WaitingThread;
|
||||
|
||||
{
|
||||
// Make counter and list operations atomic.
|
||||
LockType::ScopedLockType lock (m_mutex);
|
||||
|
||||
if (--m_counter >= 0)
|
||||
{
|
||||
// Acquired the resource so put waitingThread back.
|
||||
m_deleteList.push_front (waitingThread);
|
||||
|
||||
waitingThread = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Out of resources, go on to the waiting list.
|
||||
m_waitingThreads.push_front (waitingThread);
|
||||
}
|
||||
}
|
||||
|
||||
// Do we need to wait?
|
||||
if (waitingThread != nullptr)
|
||||
{
|
||||
// Yes so do it.
|
||||
signaled = waitingThread->wait (timeOutMilliseconds);
|
||||
|
||||
// If the wait is satisfied, then we've been taken off the
|
||||
// waiting list so put waitingThread back in the delete list.
|
||||
//
|
||||
m_deleteList.push_front (waitingThread);
|
||||
}
|
||||
|
||||
return signaled;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Semaphore::WaitingThread::WaitingThread ()
|
||||
: m_event (false) // auto-reset
|
||||
{
|
||||
}
|
||||
|
||||
bool Semaphore::WaitingThread::wait (int timeOutMilliseconds)
|
||||
{
|
||||
return m_event.wait (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
void Semaphore::WaitingThread::signal ()
|
||||
{
|
||||
m_event.signal ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// VFALCO TODO Unit Tests!
|
||||
@@ -1,82 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SEMAPHORE_BEASTHEADER
|
||||
#define BEAST_SEMAPHORE_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A semaphore.
|
||||
|
||||
This provides a traditional semaphore synchronization primitive. There is no
|
||||
upper limit on the number of signals.
|
||||
|
||||
@note There is no tryWait() or timeout facility for acquiring a resource.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API Semaphore
|
||||
{
|
||||
public:
|
||||
/** Create a semaphore with the specified number of resources.
|
||||
|
||||
@param initialCount The starting number of resources.
|
||||
*/
|
||||
explicit Semaphore (int initialCount = 0);
|
||||
|
||||
~Semaphore ();
|
||||
|
||||
/** Increase the number of available resources.
|
||||
|
||||
@param amount The number of new resources available.
|
||||
*/
|
||||
void signal (int amount = 1);
|
||||
|
||||
/** Wait for a resource.
|
||||
|
||||
A negative time-out value means that the method will wait indefinitely.
|
||||
|
||||
@returns true if the event has been signalled, false if the timeout expires.
|
||||
*/
|
||||
bool wait (int timeOutMilliseconds = -1);
|
||||
|
||||
private:
|
||||
class WaitingThread
|
||||
: public LockFreeStack <WaitingThread>::Node
|
||||
, LeakChecked <WaitingThread>
|
||||
{
|
||||
public:
|
||||
WaitingThread ();
|
||||
|
||||
bool wait (int timeOutMilliseconds);
|
||||
void signal ();
|
||||
|
||||
private:
|
||||
WaitableEvent m_event;
|
||||
};
|
||||
|
||||
typedef SpinLock LockType;
|
||||
|
||||
LockType m_mutex;
|
||||
Atomic <int> m_counter;
|
||||
LockFreeStack <WaitingThread> m_waitingThreads;
|
||||
LockFreeStack <WaitingThread> m_deleteList;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,295 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SHAREDDATA_H_INCLUDED
|
||||
#define BEAST_SHAREDDATA_H_INCLUDED
|
||||
|
||||
/** Structured access to a shared state.
|
||||
|
||||
This template wraps an object containing members representing state
|
||||
information shared between multiple threads of execution, where any thread
|
||||
may need to read or write as needed. Synchronized access to the concurrent
|
||||
state is enforced at compile time through strongly typed accessor classes.
|
||||
This interface design facilitates source code pattern matching to find all
|
||||
areas where a concurrent state is accessed.
|
||||
|
||||
There are three types of access:
|
||||
|
||||
- ReadAccess
|
||||
|
||||
Allows read access to the underlying object as `const`. ReadAccess may
|
||||
be granted to one or more threads simultaneously. If one or more
|
||||
threads have ReadAccess, requests to obtain WriteAccess are blocked.
|
||||
|
||||
- WriteAccess
|
||||
|
||||
Allows exclusive read/write access the underlying object. A WriteAccess
|
||||
request blocks until all existing ReadAccess and WriteAccess requests
|
||||
are released. While a WriteAccess exists, requests for ReadAccess
|
||||
will block.
|
||||
|
||||
- UnlockedAccess
|
||||
|
||||
Allows read access to the underlying object without using the lock.
|
||||
This can be helpful when designing concurrent structures through
|
||||
composition. It also makes it easier to search for places in code
|
||||
which use unlocked access.
|
||||
|
||||
This code example demonstrates various forms of access to a SharedData:
|
||||
|
||||
@code
|
||||
|
||||
struct SharedData
|
||||
{
|
||||
int value1;
|
||||
String value2;
|
||||
};
|
||||
|
||||
typedef SharedData <SharedData> SharedState;
|
||||
|
||||
SharedState sharedState;
|
||||
|
||||
void readExample ()
|
||||
{
|
||||
SharedState::ReadAccess state (sharedState);
|
||||
|
||||
print (state->value1); // read access
|
||||
print (state->value2); // read access
|
||||
|
||||
state->value1 = 42; // write disallowed: compile error
|
||||
}
|
||||
|
||||
void writeExample ()
|
||||
{
|
||||
SharedState::WriteAccess state (sharedState);
|
||||
|
||||
state->value2 = "Label"; // write access
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
Forwarding constructors with up to eight parameters are provided. This lets
|
||||
you write constructors into the underlying data object. For example:
|
||||
|
||||
@code
|
||||
|
||||
struct SharedData
|
||||
{
|
||||
explicit SharedData (int numSlots)
|
||||
{
|
||||
m_array.reserve (numSlots);
|
||||
}
|
||||
|
||||
std::vector <AudioSampleBuffer*> m_array;
|
||||
};
|
||||
|
||||
// Construct SharedData with one parameter
|
||||
SharedData <SharedData> sharedState (16);
|
||||
|
||||
@endcode
|
||||
|
||||
@param Object The type of object to encapsulate.
|
||||
|
||||
@warning Recursive calls are not supported. It is generally not possible for
|
||||
a thread of execution to acquire write access while it already has
|
||||
read access. Such an attempt will result in undefined behavior.
|
||||
Calling into unknown code while holding a lock can cause deadlock.
|
||||
See @ref CallQueue::queue().
|
||||
*/
|
||||
template <class Object>
|
||||
class SharedData : public Uncopyable
|
||||
{
|
||||
public:
|
||||
class ReadAccess;
|
||||
class WriteAccess;
|
||||
class UnlockedAccess;
|
||||
|
||||
/** Create a concurrent state.
|
||||
|
||||
Up to 8 parameters can be specified in the constructor. These parameters
|
||||
are forwarded to the corresponding constructor in Object. If no
|
||||
constructor in Object matches the parameter list, a compile error is
|
||||
generated.
|
||||
*/
|
||||
/** @{ */
|
||||
SharedData () { }
|
||||
|
||||
template <class T1>
|
||||
explicit SharedData (T1 t1)
|
||||
: m_obj (t1) { }
|
||||
|
||||
template <class T1, class T2>
|
||||
SharedData (T1 t1, T2 t2)
|
||||
: m_obj (t1, t2) { }
|
||||
|
||||
template <class T1, class T2, class T3>
|
||||
SharedData (T1 t1, T2 t2, T3 t3)
|
||||
: m_obj (t1, t2, t3) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
: m_obj (t1, t2, t3, t4) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
: m_obj (t1, t2, t3, t4, t5) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
: m_obj (t1, t2, t3, t4, t5, t6) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) : m_obj (t1, t2, t3, t4, t5, t6, t7) { }
|
||||
|
||||
template <class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
SharedData (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
: m_obj (t1, t2, t3, t4, t5, t6, t7, t8) { }
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
typedef ReadWriteMutex ReadWriteMutexType;
|
||||
|
||||
Object m_obj;
|
||||
ReadWriteMutexType m_mutex;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Unlocked access to a SharedData.
|
||||
|
||||
Use sparingly.
|
||||
*/
|
||||
template <class Object>
|
||||
class SharedData <Object>::UnlockedAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit UnlockedAccess (SharedData const& state)
|
||||
: m_state (state)
|
||||
{
|
||||
}
|
||||
|
||||
Object const& getObject () const
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
Object const& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
Object const* operator-> () const
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData const& m_state;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read only access to a SharedData */
|
||||
template <class Object>
|
||||
class SharedData <Object>::ReadAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
/** Create a ReadAccess from the specified SharedData */
|
||||
explicit ReadAccess (SharedData const volatile& state)
|
||||
: m_state (const_cast <SharedData const&> (state))
|
||||
, m_lock (m_state.m_mutex)
|
||||
{
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const& getObject () const
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read only smart pointer to Object */
|
||||
Object const* operator-> () const
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData const& m_state;
|
||||
ReadWriteMutexType::ScopedReadLockType m_lock;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read/write access to a SharedData */
|
||||
template <class Object>
|
||||
class SharedData <Object>::WriteAccess : public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit WriteAccess (SharedData& state)
|
||||
: m_state (state)
|
||||
, m_lock (m_state.m_mutex)
|
||||
{
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const* getObject () const
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
|
||||
/** Obtain a read only reference to Object */
|
||||
Object const& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read only smart pointer to Object */
|
||||
Object const* operator-> () const
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read/write pointer to Object */
|
||||
Object& getObject ()
|
||||
{
|
||||
return m_state.m_obj;
|
||||
}
|
||||
|
||||
/** Obtain a read/write reference to Object */
|
||||
Object& operator* ()
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
/** Obtain a read/write smart pointer to Object */
|
||||
Object* operator-> ()
|
||||
{
|
||||
return &getObject ();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedData& m_state;
|
||||
ReadWriteMutexType::ScopedWriteLockType m_lock;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,105 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
void ThreadGroup::QuitType::operator () (Worker* worker)
|
||||
{
|
||||
worker->setShouldExit ();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
ThreadGroup::Worker::Worker (String name, ThreadGroup& group)
|
||||
: Thread (name)
|
||||
, m_group (group)
|
||||
, m_shouldExit (false)
|
||||
{
|
||||
startThread ();
|
||||
}
|
||||
|
||||
ThreadGroup::Worker::~Worker ()
|
||||
{
|
||||
// Make sure the thread is stopped.
|
||||
stopThread (-1);
|
||||
}
|
||||
|
||||
void ThreadGroup::Worker::setShouldExit ()
|
||||
{
|
||||
m_shouldExit = true;
|
||||
}
|
||||
|
||||
void ThreadGroup::Worker::run ()
|
||||
{
|
||||
do
|
||||
{
|
||||
m_group.m_semaphore.wait ();
|
||||
|
||||
Work* work = m_group.m_queue.pop_front ();
|
||||
|
||||
bassert (work != nullptr);
|
||||
|
||||
work->operator () (this);
|
||||
|
||||
delete work;
|
||||
}
|
||||
while (!m_shouldExit);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
ThreadGroup::ThreadGroup (int numberOfThreads)
|
||||
: m_numberOfThreads (numberOfThreads)
|
||||
, m_semaphore (0)
|
||||
{
|
||||
for (int i = 0; i++ < numberOfThreads; )
|
||||
{
|
||||
String s;
|
||||
s << "ThreadGroup (" << i << ")";
|
||||
|
||||
m_threads.push_front (new Worker (s, *this));
|
||||
}
|
||||
}
|
||||
|
||||
ThreadGroup::~ThreadGroup ()
|
||||
{
|
||||
// Put one quit item in the queue for each worker to stop.
|
||||
for (int i = 0; i < m_numberOfThreads; ++i)
|
||||
{
|
||||
m_queue.push_front (new (getAllocator ()) QuitType);
|
||||
|
||||
m_semaphore.signal ();
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Worker* worker = m_threads.pop_front ();
|
||||
|
||||
if (worker != nullptr)
|
||||
delete worker;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// There must not be pending work!
|
||||
bassert (m_queue.pop_front () == nullptr);
|
||||
}
|
||||
|
||||
int ThreadGroup::getNumberOfThreads () const
|
||||
{
|
||||
return m_numberOfThreads;
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_THREADGROUP_BEASTHEADER
|
||||
#define BEAST_THREADGROUP_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
@ingroup beast_concurrent
|
||||
|
||||
@brief A group of threads for parallelizing tasks.
|
||||
|
||||
@see ParallelFor
|
||||
*/
|
||||
class BEAST_API ThreadGroup
|
||||
{
|
||||
public:
|
||||
typedef FifoFreeStoreType AllocatorType;
|
||||
|
||||
/** Creates the specified number of threads.
|
||||
|
||||
@param numberOfThreads The number of threads in the group. This must be
|
||||
greater than zero. If this parameter is omitted,
|
||||
one thread is created per available CPU.
|
||||
*/
|
||||
explicit ThreadGroup (int numberOfThreads = SystemStats::getNumCpus ());
|
||||
|
||||
~ThreadGroup ();
|
||||
|
||||
/** Allocator access.
|
||||
*/
|
||||
inline AllocatorType& getAllocator ()
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
/** Determine the number of threads in the group.
|
||||
|
||||
@return The number of threads in the group.
|
||||
*/
|
||||
int getNumberOfThreads () const;
|
||||
|
||||
/** Calls a functor on multiple threads.
|
||||
|
||||
The specified functor is executed on some or all available threads at once.
|
||||
A call is always guaranteed to execute.
|
||||
|
||||
@param maxThreads The maximum number of threads to use, or -1 for all.
|
||||
|
||||
@param f The functor to call for each thread.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Functor>
|
||||
void callf (int maxThreads, Functor f)
|
||||
{
|
||||
bassert (maxThreads > 0 || maxThreads == -1);
|
||||
|
||||
int numberOfThreads = getNumberOfThreads ();
|
||||
|
||||
if (maxThreads != -1 && maxThreads < numberOfThreads)
|
||||
numberOfThreads = maxThreads;
|
||||
|
||||
while (numberOfThreads--)
|
||||
{
|
||||
m_queue.push_front (new (getAllocator ()) WorkType <Functor> (f));
|
||||
m_semaphore.signal ();
|
||||
}
|
||||
}
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Fn>
|
||||
void call (int maxThreads, Fn f)
|
||||
{ callf (maxThreads, functional::bind (f)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Fn, class T1>
|
||||
void call (int maxThreads, Fn f, T1 t1)
|
||||
{ callf (maxThreads, functional::bind (f, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Fn, class T1, class T2>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Fn, class T1, class T2, class T3>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2, T3 t3)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Fn, class T1, class T2, class T3, class T4>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
void call (int maxThreads, Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ callf (maxThreads, functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
void stopThreads (int numberOfThreadsToStop);
|
||||
|
||||
//============================================================================
|
||||
private:
|
||||
/** A thread in the group.
|
||||
*/
|
||||
class Worker
|
||||
: public LockFreeStack <Worker>::Node
|
||||
, public Thread
|
||||
, LeakChecked <Worker>
|
||||
{
|
||||
public:
|
||||
Worker (String name, ThreadGroup& group);
|
||||
~Worker ();
|
||||
|
||||
void setShouldExit ();
|
||||
|
||||
private:
|
||||
void run ();
|
||||
|
||||
private:
|
||||
ThreadGroup& m_group;
|
||||
bool m_shouldExit;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
private:
|
||||
/** Abstract work item.
|
||||
*/
|
||||
class Work : public LockFreeStack <Work>::Node
|
||||
, public AllocatedBy <AllocatorType>
|
||||
{
|
||||
public:
|
||||
virtual ~Work () { }
|
||||
|
||||
/* The worker is passed in so we can make it quit later.
|
||||
*/
|
||||
virtual void operator () (Worker* worker) = 0;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
class WorkType : public Work, LeakChecked <WorkType <Functor> >
|
||||
{
|
||||
public:
|
||||
explicit WorkType (Functor const& f) : m_f (f) { }
|
||||
~WorkType () { }
|
||||
void operator () (Worker*)
|
||||
{
|
||||
m_f ();
|
||||
}
|
||||
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
/** Used to make a Worker stop
|
||||
*/
|
||||
class QuitType
|
||||
: public Work
|
||||
, LeakChecked <QuitType>
|
||||
{
|
||||
public:
|
||||
void operator () (Worker* worker);
|
||||
};
|
||||
|
||||
private:
|
||||
int const m_numberOfThreads;
|
||||
Semaphore m_semaphore;
|
||||
AllocatorType m_allocator;
|
||||
LockFreeStack <Work> m_queue;
|
||||
LockFreeStack <Worker> m_threads;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,152 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ThreadWithCallQueue::ThreadWithCallQueue (String name)
|
||||
: CallQueue (name)
|
||||
, m_thread (name)
|
||||
, m_entryPoints (nullptr)
|
||||
, m_calledStart (false)
|
||||
, m_calledStop (false)
|
||||
, m_shouldStop (false)
|
||||
{
|
||||
}
|
||||
|
||||
ThreadWithCallQueue::~ThreadWithCallQueue ()
|
||||
{
|
||||
stop (true);
|
||||
}
|
||||
|
||||
ThreadWithCallQueue::EntryPoints* ThreadWithCallQueue::getDefaultEntryPoints () noexcept
|
||||
{
|
||||
static EntryPoints entryPoints;
|
||||
|
||||
return &entryPoints;
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::start (EntryPoints* const entryPoints)
|
||||
{
|
||||
{
|
||||
// This is mostly for diagnostics
|
||||
// TODO: Atomic flag for this whole thing
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
|
||||
// start() MUST be called.
|
||||
bassert (!m_calledStart);
|
||||
m_calledStart = true;
|
||||
}
|
||||
|
||||
m_entryPoints = entryPoints;
|
||||
|
||||
m_thread.start (this);
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::stop (bool const wait)
|
||||
{
|
||||
// can't call stop(true) from within a thread function
|
||||
bassert (!wait || !m_thread.isTheCurrentThread ());
|
||||
|
||||
{
|
||||
CriticalSection::ScopedLockType lock (m_mutex);
|
||||
|
||||
// start() MUST be called.
|
||||
bassert (m_calledStart);
|
||||
|
||||
// TODO: Atomic for this
|
||||
if (!m_calledStop)
|
||||
{
|
||||
m_calledStop = true;
|
||||
|
||||
{
|
||||
CriticalSection::ScopedUnlockType unlock (m_mutex); // getting fancy
|
||||
|
||||
call (&ThreadWithCallQueue::doStop, this);
|
||||
|
||||
// in theory something could slip in here
|
||||
|
||||
close ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wait)
|
||||
m_thread.join ();
|
||||
}
|
||||
|
||||
// Should be called periodically by the idle function.
|
||||
// There are three possible results:
|
||||
//
|
||||
// #1 Returns false. The idle function may continue or return.
|
||||
// #2 Returns true. The idle function should return as soon as possible.
|
||||
// #3 Throws a Thread::Interruption exception.
|
||||
//
|
||||
// If interruptionPoint returns true or throws, it must
|
||||
// not be called again before the thread has the opportunity to reset.
|
||||
//
|
||||
bool ThreadWithCallQueue::interruptionPoint ()
|
||||
{
|
||||
return m_thread.interruptionPoint ();
|
||||
}
|
||||
|
||||
// Interrupts the idle function by queueing a call that does nothing.
|
||||
void ThreadWithCallQueue::interrupt ()
|
||||
{
|
||||
call (&ThreadWithCallQueue::doNothing);
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::doNothing ()
|
||||
{
|
||||
// Intentionally empty
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::signal ()
|
||||
{
|
||||
m_thread.interrupt ();
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::reset ()
|
||||
{
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::doStop ()
|
||||
{
|
||||
m_shouldStop = true;
|
||||
}
|
||||
|
||||
void ThreadWithCallQueue::threadRun ()
|
||||
{
|
||||
m_entryPoints->threadInit ();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CallQueue::synchronize ();
|
||||
|
||||
if (m_shouldStop)
|
||||
break;
|
||||
|
||||
bool interrupted = m_entryPoints->threadIdle ();
|
||||
|
||||
if (!interrupted)
|
||||
interrupted = interruptionPoint ();
|
||||
|
||||
if (!interrupted)
|
||||
m_thread.wait ();
|
||||
}
|
||||
|
||||
m_entryPoints->threadExit ();
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_THREADWITHCALLQUEUE_BEASTHEADER
|
||||
#define BEAST_THREADWITHCALLQUEUE_BEASTHEADER
|
||||
|
||||
/** An InterruptibleThread with a CallQueue.
|
||||
|
||||
This combines an InterruptibleThread with a CallQueue, allowing functors to
|
||||
be queued for asynchronous execution on the thread.
|
||||
|
||||
The thread runs an optional user-defined idle function, which must regularly
|
||||
check for an interruption using the InterruptibleThread interface. When an
|
||||
interruption is signaled, the idle function returns and the CallQueue is
|
||||
synchronized. Then, the idle function is resumed.
|
||||
|
||||
When the ThreadWithCallQueue first starts up, an optional user-defined
|
||||
initialization function is executed on the thread. When the thread exits,
|
||||
a user-defined exit function may be executed on the thread.
|
||||
|
||||
@see CallQueue
|
||||
|
||||
@ingroup beast_concurrent
|
||||
*/
|
||||
class BEAST_API ThreadWithCallQueue
|
||||
: public CallQueue
|
||||
, private InterruptibleThread::EntryPoint
|
||||
, LeakChecked <ThreadWithCallQueue>
|
||||
{
|
||||
public:
|
||||
/** Entry points for a ThreadWithCallQueue.
|
||||
*/
|
||||
class EntryPoints
|
||||
{
|
||||
public:
|
||||
virtual ~EntryPoints () { }
|
||||
|
||||
virtual void threadInit () { }
|
||||
|
||||
virtual void threadExit () { }
|
||||
|
||||
virtual bool threadIdle ()
|
||||
{
|
||||
bool interrupted = false;
|
||||
|
||||
return interrupted;
|
||||
}
|
||||
};
|
||||
|
||||
/** Create a thread.
|
||||
|
||||
@param name The name of the InterruptibleThread and CallQueue, used
|
||||
for diagnostics when debugging.
|
||||
*/
|
||||
explicit ThreadWithCallQueue (String name);
|
||||
|
||||
/** Retrieve the default entry points.
|
||||
|
||||
The default entry points do nothing.
|
||||
*/
|
||||
static EntryPoints* getDefaultEntryPoints () noexcept;
|
||||
|
||||
/** Destroy a ThreadWithCallQueue.
|
||||
|
||||
If the thread is still running it is stopped. The destructor blocks
|
||||
until the thread exits cleanly.
|
||||
*/
|
||||
~ThreadWithCallQueue ();
|
||||
|
||||
/** Start the thread, with optional entry points.
|
||||
|
||||
If `entryPoints` is specified then the thread runs using those
|
||||
entry points. If ommitted, the default entry simply do nothing.
|
||||
This is useful for creating a thread whose sole activities are
|
||||
performed through the call queue.
|
||||
|
||||
@param entryPoints An optional pointer to @ref EntryPoints.
|
||||
*/
|
||||
void start (EntryPoints* const entryPoints = getDefaultEntryPoints ());
|
||||
|
||||
/* Stop the thread.
|
||||
|
||||
Stops the thread and optionally wait until it exits. It is safe to call
|
||||
this function at any time and as many times as desired.
|
||||
|
||||
After a call to stop () the CallQueue is closed, and attempts to queue new
|
||||
functors will throw a runtime exception. Existing functors will still
|
||||
execute.
|
||||
|
||||
Any listeners registered on the CallQueue need to be removed
|
||||
before stop is called
|
||||
|
||||
@invariant The caller is not on the associated thread.
|
||||
|
||||
@param wait `true` if the function should wait until the thread exits
|
||||
before returning.
|
||||
*/
|
||||
|
||||
void stop (bool const wait);
|
||||
|
||||
/** Determine if the thread needs interruption.
|
||||
|
||||
Should be called periodically by the idle function. If interruptionPoint
|
||||
returns true or throws, it must not be called again until the idle function
|
||||
returns and is re-entered.
|
||||
|
||||
@invariant No previous calls to interruptionPoint() made after the idle
|
||||
function entry point returned `true`.
|
||||
|
||||
@return `false` if the idle function may continue, or `true` if the
|
||||
idle function must return as soon as possible.
|
||||
*/
|
||||
bool interruptionPoint ();
|
||||
|
||||
/* Interrupts the idle function.
|
||||
*/
|
||||
void interrupt ();
|
||||
|
||||
private:
|
||||
static void doNothing ();
|
||||
|
||||
void signal ();
|
||||
|
||||
void reset ();
|
||||
|
||||
void doStop ();
|
||||
|
||||
void threadRun ();
|
||||
|
||||
private:
|
||||
InterruptibleThread m_thread;
|
||||
EntryPoints* m_entryPoints;
|
||||
bool m_calledStart;
|
||||
bool m_calledStop;
|
||||
bool m_shouldStop;
|
||||
CriticalSection m_mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,317 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#if defined (BEAST_CORE_BEASTHEADER) && ! BEAST_AMALGAMATED_INCLUDE
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of BEAST cpp file"
|
||||
#endif
|
||||
|
||||
// Your project must contain a BeastConfig.h file with your project-specific settings in it,
|
||||
// and your header search path must make it accessible to the module's files.
|
||||
#include "BeastConfig.h"
|
||||
|
||||
//==============================================================================
|
||||
#include "native/beast_BasicNativeHeaders.h"
|
||||
#include "beast_core.h"
|
||||
|
||||
#include <locale>
|
||||
#include <cctype>
|
||||
|
||||
#if ! BEAST_BSD
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#if ! BEAST_ANDROID
|
||||
#include <cwctype>
|
||||
#endif
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
#include <ctime>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#if ! BEAST_MINGW
|
||||
#include <Dbghelp.h>
|
||||
|
||||
#if ! BEAST_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment (lib, "DbgHelp.lib")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if BEAST_MINGW
|
||||
#include <ws2spi.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if BEAST_LINUX || BEAST_ANDROID
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/errno.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#if BEAST_LINUX
|
||||
#include <langinfo.h>
|
||||
#endif
|
||||
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/time.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#if ! BEAST_ANDROID && ! BEAST_BSD
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if BEAST_MAC || BEAST_IOS
|
||||
#include <xlocale.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
#if BEAST_ANDROID
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// If the MSVC debug heap headers were included, disable
|
||||
// the macros during the juce include since they conflict.
|
||||
#ifdef _CRTDBG_MAP_ALLOC
|
||||
#pragma push_macro("calloc")
|
||||
#pragma push_macro("free")
|
||||
#pragma push_macro("malloc")
|
||||
#pragma push_macro("realloc")
|
||||
#pragma push_macro("_recalloc")
|
||||
#pragma push_macro("_aligned_free")
|
||||
#pragma push_macro("_aligned_malloc")
|
||||
#pragma push_macro("_aligned_offset_malloc")
|
||||
#pragma push_macro("_aligned_realloc")
|
||||
#pragma push_macro("_aligned_recalloc")
|
||||
#pragma push_macro("_aligned_offset_realloc")
|
||||
#pragma push_macro("_aligned_offset_recalloc")
|
||||
#pragma push_macro("_aligned_msize")
|
||||
#undef calloc
|
||||
#undef free
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef _recalloc
|
||||
#undef _aligned_free
|
||||
#undef _aligned_malloc
|
||||
#undef _aligned_offset_malloc
|
||||
#undef _aligned_realloc
|
||||
#undef _aligned_recalloc
|
||||
#undef _aligned_offset_realloc
|
||||
#undef _aligned_offset_recalloc
|
||||
#undef _aligned_msize
|
||||
#endif
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
#include "containers/beast_AbstractFifo.cpp"
|
||||
#include "containers/beast_DynamicObject.cpp"
|
||||
#include "containers/beast_NamedValueSet.cpp"
|
||||
#include "containers/beast_PropertySet.cpp"
|
||||
#include "containers/beast_Variant.cpp"
|
||||
|
||||
#include "diagnostic/beast_Debug.cpp"
|
||||
#include "diagnostic/beast_Error.cpp"
|
||||
#include "diagnostic/beast_FatalError.cpp"
|
||||
#include "diagnostic/beast_FPUFlags.cpp"
|
||||
#include "diagnostic/beast_LeakChecked.cpp"
|
||||
#include "diagnostic/beast_ProtectedCall.cpp"
|
||||
#include "diagnostic/beast_SemanticVersion.cpp"
|
||||
#include "diagnostic/beast_UnitTest.cpp"
|
||||
#include "diagnostic/beast_UnitTestUtilities.cpp"
|
||||
|
||||
#include "files/beast_DirectoryIterator.cpp"
|
||||
#include "files/beast_File.cpp"
|
||||
#include "files/beast_FileInputStream.cpp"
|
||||
#include "files/beast_FileOutputStream.cpp"
|
||||
#include "files/beast_FileSearchPath.cpp"
|
||||
#include "files/beast_RandomAccessFile.cpp"
|
||||
#include "files/beast_TemporaryFile.cpp"
|
||||
|
||||
#include "json/beast_JSON.cpp"
|
||||
|
||||
#include "logging/beast_FileLogger.cpp"
|
||||
#include "logging/beast_Logger.cpp"
|
||||
|
||||
#include "maths/beast_BigInteger.cpp"
|
||||
#include "maths/beast_Expression.cpp"
|
||||
#include "maths/beast_MurmurHash.cpp"
|
||||
#include "maths/beast_Random.cpp"
|
||||
|
||||
#include "memory/beast_MemoryBlock.cpp"
|
||||
|
||||
#include "misc/beast_Main.cpp"
|
||||
#include "misc/beast_Result.cpp"
|
||||
#include "misc/beast_Uuid.cpp"
|
||||
|
||||
#include "network/beast_MACAddress.cpp"
|
||||
#include "network/beast_NamedPipe.cpp"
|
||||
#include "network/beast_Socket.cpp"
|
||||
#include "network/beast_URL.cpp"
|
||||
#include "network/beast_IPAddress.cpp"
|
||||
|
||||
#include "streams/beast_BufferedInputStream.cpp"
|
||||
#include "streams/beast_FileInputSource.cpp"
|
||||
#include "streams/beast_InputStream.cpp"
|
||||
#include "streams/beast_MemoryInputStream.cpp"
|
||||
#include "streams/beast_MemoryOutputStream.cpp"
|
||||
#include "streams/beast_OutputStream.cpp"
|
||||
#include "streams/beast_SubregionStream.cpp"
|
||||
|
||||
#include "system/beast_SystemStats.cpp"
|
||||
|
||||
#include "text/beast_CharacterFunctions.cpp"
|
||||
#include "text/beast_LexicalCast.cpp"
|
||||
#include "text/beast_Identifier.cpp"
|
||||
#include "text/beast_LocalisedStrings.cpp"
|
||||
#include "text/beast_String.cpp"
|
||||
#include "text/beast_StringArray.cpp"
|
||||
#include "text/beast_StringPairArray.cpp"
|
||||
#include "text/beast_StringPool.cpp"
|
||||
#include "text/beast_TextDiff.cpp"
|
||||
|
||||
#include "threads/beast_ChildProcess.cpp"
|
||||
#include "threads/beast_ReadWriteLock.cpp"
|
||||
#include "threads/beast_SpinDelay.cpp"
|
||||
#include "threads/beast_Thread.cpp"
|
||||
#include "threads/beast_ThreadPool.cpp"
|
||||
#include "threads/beast_TimeSliceThread.cpp"
|
||||
|
||||
#include "time/beast_PerformanceCounter.cpp"
|
||||
#include "time/beast_PerformedAtExit.cpp"
|
||||
#include "time/beast_RelativeTime.cpp"
|
||||
#include "time/beast_Time.cpp"
|
||||
|
||||
#include "xml/beast_XmlDocument.cpp"
|
||||
#include "xml/beast_XmlElement.cpp"
|
||||
|
||||
#include "zip/beast_GZIPDecompressorInputStream.cpp"
|
||||
#include "zip/beast_GZIPCompressorOutputStream.cpp"
|
||||
#include "zip/beast_ZipFile.cpp"
|
||||
|
||||
#if BEAST_MAC || BEAST_IOS
|
||||
#include "native/beast_osx_ObjCHelpers.h"
|
||||
#endif
|
||||
|
||||
#if BEAST_WINDOWS
|
||||
#include "native/beast_win32_FPUFlags.cpp"
|
||||
#else
|
||||
#include "native/beast_posix_FPUFlags.cpp"
|
||||
#endif
|
||||
|
||||
#if BEAST_ANDROID
|
||||
#include "native/beast_android_JNIHelpers.h"
|
||||
#endif
|
||||
|
||||
#if ! BEAST_WINDOWS
|
||||
#include "native/beast_posix_SharedCode.h"
|
||||
#include "native/beast_posix_NamedPipe.cpp"
|
||||
#endif
|
||||
|
||||
#if BEAST_MAC || BEAST_IOS
|
||||
#include "native/beast_mac_Files.mm"
|
||||
#include "native/beast_mac_Network.mm"
|
||||
#include "native/beast_mac_Strings.mm"
|
||||
#include "native/beast_mac_SystemStats.mm"
|
||||
#include "native/beast_mac_Threads.mm"
|
||||
|
||||
#elif BEAST_WINDOWS
|
||||
#include "native/beast_win32_ComSmartPtr.h"
|
||||
#include "native/beast_win32_Files.cpp"
|
||||
#include "native/beast_win32_Network.cpp"
|
||||
#include "native/beast_win32_Registry.cpp"
|
||||
#include "native/beast_win32_SystemStats.cpp"
|
||||
#include "native/beast_win32_Threads.cpp"
|
||||
|
||||
#elif BEAST_LINUX
|
||||
#include "native/beast_linux_Files.cpp"
|
||||
#include "native/beast_linux_Network.cpp"
|
||||
#include "native/beast_linux_SystemStats.cpp"
|
||||
#include "native/beast_linux_Threads.cpp"
|
||||
|
||||
#elif BEAST_BSD
|
||||
#include "native/beast_bsd_Files.cpp"
|
||||
#include "native/beast_bsd_Network.cpp"
|
||||
#include "native/beast_bsd_SystemStats.cpp"
|
||||
#include "native/beast_bsd_Threads.cpp"
|
||||
|
||||
#elif BEAST_ANDROID
|
||||
#include "native/beast_android_Files.cpp"
|
||||
#include "native/beast_android_Misc.cpp"
|
||||
#include "native/beast_android_Network.cpp"
|
||||
#include "native/beast_android_SystemStats.cpp"
|
||||
#include "native/beast_android_Threads.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
#include "threads/beast_HighResolutionTimer.cpp"
|
||||
|
||||
}
|
||||
|
||||
#ifdef _CRTDBG_MAP_ALLOC
|
||||
#pragma pop_macro("calloc")
|
||||
#pragma pop_macro("free")
|
||||
#pragma pop_macro("malloc")
|
||||
#pragma pop_macro("realloc")
|
||||
#pragma pop_macro("_recalloc")
|
||||
#pragma pop_macro("_aligned_free")
|
||||
#pragma pop_macro("_aligned_malloc")
|
||||
#pragma pop_macro("_aligned_offset_malloc")
|
||||
#pragma pop_macro("_aligned_realloc")
|
||||
#pragma pop_macro("_aligned_recalloc")
|
||||
#pragma pop_macro("_aligned_offset_realloc")
|
||||
#pragma pop_macro("_aligned_offset_recalloc")
|
||||
#pragma pop_macro("_aligned_msize")
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BEAST_BOOST_IS_AVAILABLE
|
||||
namespace boost {
|
||||
namespace placeholders {
|
||||
boost::arg<1> _1;
|
||||
boost::arg<2> _2;
|
||||
boost::arg<3> _3;
|
||||
boost::arg<4> _4;
|
||||
boost::arg<5> _5;
|
||||
boost::arg<6> _6;
|
||||
boost::arg<7> _7;
|
||||
boost::arg<8> _8;
|
||||
boost::arg<9> _9;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,364 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_CORE_H_INCLUDED
|
||||
#define BEAST_CORE_H_INCLUDED
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* If you fail to make sure that all your compile units are building Beast with
|
||||
the same set of option flags, then there's a risk that different compile
|
||||
units will treat the classes as having different memory layouts, leading to
|
||||
very nasty memory corruption errors when they all get linked together.
|
||||
That's why it's best to always include the BeastConfig.h file before any
|
||||
beast headers.
|
||||
*/
|
||||
#ifndef BEAST_BEASTCONFIG_H_INCLUDED
|
||||
# ifdef _MSC_VER
|
||||
# pragma message ("Have you included your BeastConfig.h file before including the Beast headers?")
|
||||
# else
|
||||
# warning "Have you included your BeastConfig.h file before including the Beast headers?"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "system/beast_TargetPlatform.h"
|
||||
|
||||
//
|
||||
// Apply sensible defaults for the configuration settings
|
||||
//
|
||||
|
||||
#ifndef BEAST_LOG_ASSERTIONS
|
||||
# if BEAST_ANDROID
|
||||
# define BEAST_LOG_ASSERTIONS 1
|
||||
# else
|
||||
# define BEAST_LOG_ASSERTIONS 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BEAST_DEBUG && ! defined (BEAST_CHECK_MEMORY_LEAKS)
|
||||
#define BEAST_CHECK_MEMORY_LEAKS 1
|
||||
#endif
|
||||
|
||||
#ifndef BEAST_INCLUDE_ZLIB_CODE
|
||||
#define BEAST_INCLUDE_ZLIB_CODE 1
|
||||
#endif
|
||||
|
||||
#ifndef BEAST_ZLIB_INCLUDE_PATH
|
||||
#define BEAST_ZLIB_INCLUDE_PATH <zlib.h>
|
||||
#endif
|
||||
|
||||
/* Config: BEAST_CATCH_UNHANDLED_EXCEPTIONS
|
||||
If enabled, this will add some exception-catching code to forward unhandled exceptions
|
||||
to your BEASTApplication::unhandledException() callback.
|
||||
*/
|
||||
#ifndef BEAST_CATCH_UNHANDLED_EXCEPTIONS
|
||||
//#define BEAST_CATCH_UNHANDLED_EXCEPTIONS 1
|
||||
#endif
|
||||
|
||||
#ifndef BEAST_BOOST_IS_AVAILABLE
|
||||
#define BEAST_BOOST_IS_AVAILABLE 0
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// This is a hack to fix boost's goofy placeholders
|
||||
//
|
||||
|
||||
#if BEAST_BOOST_IS_AVAILABLE
|
||||
#ifdef BOOST_BIND_PLACEHOLDERS_HPP_INCLUDED
|
||||
#error <boost/bind.hpp> must not be included before this file
|
||||
#endif
|
||||
// Prevent <boost/bind/placeholders.hpp> from being included
|
||||
#define BOOST_BIND_PLACEHOLDERS_HPP_INCLUDED
|
||||
#include <boost/bind/arg.hpp>
|
||||
#include <boost/config.hpp>
|
||||
// This based on <boost/bind/placeholders.cpp>
|
||||
namespace boost {
|
||||
namespace placeholders {
|
||||
extern boost::arg<1> _1;
|
||||
extern boost::arg<2> _2;
|
||||
extern boost::arg<3> _3;
|
||||
extern boost::arg<4> _4;
|
||||
extern boost::arg<5> _5;
|
||||
extern boost::arg<6> _6;
|
||||
extern boost::arg<7> _7;
|
||||
extern boost::arg<8> _8;
|
||||
extern boost::arg<9> _9;
|
||||
}
|
||||
using namespace placeholders;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Choose a source of bind, placeholders, and function
|
||||
//
|
||||
|
||||
#if !BEAST_BIND_USES_STD && !BEAST_BIND_USES_TR1 && !BEAST_BIND_USES_BOOST
|
||||
# if BEAST_MSVC
|
||||
# define BEAST_BIND_USES_STD 1
|
||||
# elif BEAST_IOS || BEAST_MAC
|
||||
# include <ciso646> // detect version of std::lib
|
||||
# if BEAST_IOS && BEAST_BOOST_IS_AVAILABLE // Work-around for iOS bugs with bind.
|
||||
# define BEAST_BIND_USES_BOOST 1
|
||||
# elif _LIBCPP_VERSION // libc++
|
||||
# define BEAST_BIND_USES_STD 1
|
||||
# else // libstdc++ (GNU)
|
||||
# define BEAST_BIND_USES_TR1 1
|
||||
# endif
|
||||
# elif BEAST_LINUX || BEAST_BSD
|
||||
# define BEAST_BIND_USES_TR1 1
|
||||
# else
|
||||
# define BEAST_BIND_USES_STD 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BEAST_BIND_USES_STD
|
||||
# include <functional>
|
||||
#elif BEAST_BIND_USES_TR1
|
||||
# include <tr1/functional>
|
||||
#elif BEAST_BIND_USES_BOOST
|
||||
# include <boost/bind.hpp>
|
||||
# include <boost/function.hpp>
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "system/beast_StandardHeader.h"
|
||||
|
||||
#if BEAST_MSVC
|
||||
# pragma warning (disable: 4251) // (DLL build warning, must be disabled before pushing the warning state)
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4786) // (long class name warning)
|
||||
# ifdef __INTEL_COMPILER
|
||||
# pragma warning (disable: 1125)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// If the MSVC debug heap headers were included, disable
|
||||
// the macros during the juce include since they conflict.
|
||||
#ifdef _CRTDBG_MAP_ALLOC
|
||||
#pragma push_macro("calloc")
|
||||
#pragma push_macro("free")
|
||||
#pragma push_macro("malloc")
|
||||
#pragma push_macro("realloc")
|
||||
#pragma push_macro("_recalloc")
|
||||
#pragma push_macro("_aligned_free")
|
||||
#pragma push_macro("_aligned_malloc")
|
||||
#pragma push_macro("_aligned_offset_malloc")
|
||||
#pragma push_macro("_aligned_realloc")
|
||||
#pragma push_macro("_aligned_recalloc")
|
||||
#pragma push_macro("_aligned_offset_realloc")
|
||||
#pragma push_macro("_aligned_offset_recalloc")
|
||||
#pragma push_macro("_aligned_msize")
|
||||
#undef calloc
|
||||
#undef free
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef _recalloc
|
||||
#undef _aligned_free
|
||||
#undef _aligned_malloc
|
||||
#undef _aligned_offset_malloc
|
||||
#undef _aligned_realloc
|
||||
#undef _aligned_recalloc
|
||||
#undef _aligned_offset_realloc
|
||||
#undef _aligned_offset_recalloc
|
||||
#undef _aligned_msize
|
||||
#endif
|
||||
|
||||
namespace beast
|
||||
{
|
||||
|
||||
// Order matters, since headers don't have their own #include lines.
|
||||
// Add new includes to the bottom.
|
||||
|
||||
#include "memory/beast_Uncopyable.h"
|
||||
|
||||
#include "system/beast_PlatformDefs.h"
|
||||
#include "system/beast_TargetPlatform.h"
|
||||
#include "system/beast_Functional.h"
|
||||
|
||||
#include "maths/beast_MathsFunctions.h"
|
||||
#include "memory/beast_Atomic.h"
|
||||
#include "memory/beast_AtomicCounter.h"
|
||||
#include "memory/beast_AtomicFlag.h"
|
||||
#include "memory/beast_AtomicPointer.h"
|
||||
#include "memory/beast_AtomicState.h"
|
||||
#include "containers/beast_LockFreeStack.h"
|
||||
#include "threads/beast_SpinDelay.h"
|
||||
#include "memory/beast_StaticObject.h"
|
||||
#include "memory/beast_Memory.h"
|
||||
|
||||
#include "text/beast_String.h"
|
||||
|
||||
#include "threads/beast_CriticalSection.h"
|
||||
#include "diagnostic/beast_FatalError.h"
|
||||
#include "diagnostic/beast_SafeBool.h"
|
||||
#include "diagnostic/beast_Error.h"
|
||||
#include "diagnostic/beast_Debug.h"
|
||||
#include "diagnostic/beast_Throw.h"
|
||||
|
||||
#include "text/beast_CharacterFunctions.h"
|
||||
#include "text/beast_CharPointer_ASCII.h"
|
||||
#include "text/beast_CharPointer_UTF16.h"
|
||||
#include "text/beast_CharPointer_UTF32.h"
|
||||
#include "text/beast_CharPointer_UTF8.h"
|
||||
#include "text/beast_LexicalCast.h"
|
||||
|
||||
#include "time/beast_PerformedAtExit.h"
|
||||
#include "diagnostic/beast_LeakChecked.h"
|
||||
#include "memory/beast_ByteOrder.h"
|
||||
#include "memory/beast_ByteSwap.h"
|
||||
#include "maths/beast_uint24.h"
|
||||
#include "logging/beast_Logger.h"
|
||||
#include "threads/beast_Thread.h"
|
||||
#include "diagnostic/beast_FPUFlags.h"
|
||||
#include "diagnostic/beast_ProtectedCall.h"
|
||||
#include "containers/beast_AbstractFifo.h"
|
||||
#include "containers/beast_Array.h"
|
||||
#include "containers/beast_ArrayAllocationBase.h"
|
||||
#include "containers/beast_DynamicObject.h"
|
||||
#include "containers/beast_ElementComparator.h"
|
||||
#include "maths/beast_Random.h"
|
||||
#include "containers/beast_HashMap.h"
|
||||
#include "containers/beast_List.h"
|
||||
#include "containers/beast_LinkedListPointer.h"
|
||||
#include "containers/beast_LockFreeQueue.h"
|
||||
#include "containers/beast_NamedValueSet.h"
|
||||
#include "containers/beast_OwnedArray.h"
|
||||
#include "containers/beast_PropertySet.h"
|
||||
#include "containers/beast_SharedObjectArray.h"
|
||||
#include "containers/beast_ScopedValueSetter.h"
|
||||
#include "containers/beast_SharedTable.h"
|
||||
#include "containers/beast_SortedLookupTable.h"
|
||||
#include "containers/beast_SortedSet.h"
|
||||
#include "containers/beast_SparseSet.h"
|
||||
#include "containers/beast_Variant.h"
|
||||
#include "files/beast_DirectoryIterator.h"
|
||||
#include "files/beast_File.h"
|
||||
#include "files/beast_FileInputStream.h"
|
||||
#include "files/beast_FileOutputStream.h"
|
||||
#include "files/beast_FileSearchPath.h"
|
||||
#include "files/beast_MemoryMappedFile.h"
|
||||
#include "files/beast_RandomAccessFile.h"
|
||||
#include "files/beast_TemporaryFile.h"
|
||||
#include "json/beast_JSON.h"
|
||||
#include "logging/beast_FileLogger.h"
|
||||
#include "logging/beast_Logger.h"
|
||||
#include "maths/beast_BigInteger.h"
|
||||
#include "maths/beast_Expression.h"
|
||||
#include "maths/beast_Interval.h"
|
||||
#include "maths/beast_MathsFunctions.h"
|
||||
#include "maths/beast_MurmurHash.h"
|
||||
#include "maths/beast_Range.h"
|
||||
#include "memory/beast_ByteOrder.h"
|
||||
#include "memory/beast_HeapBlock.h"
|
||||
#include "memory/beast_Memory.h"
|
||||
#include "memory/beast_MemoryBlock.h"
|
||||
#include "memory/beast_OptionalScopedPointer.h"
|
||||
#include "memory/beast_SharedObject.h"
|
||||
#include "memory/beast_ScopedPointer.h"
|
||||
#include "threads/beast_SpinLock.h"
|
||||
#include "memory/beast_SharedSingleton.h"
|
||||
#include "memory/beast_WeakReference.h"
|
||||
#include "memory/beast_MemoryAlignment.h"
|
||||
#include "memory/beast_CacheLine.h"
|
||||
#include "memory/beast_RecycledObjectPool.h"
|
||||
#include "misc/beast_Main.h"
|
||||
#include "misc/beast_Result.h"
|
||||
#include "misc/beast_Uuid.h"
|
||||
#include "misc/beast_WindowsRegistry.h"
|
||||
#include "network/beast_IPAddress.h"
|
||||
#include "network/beast_MACAddress.h"
|
||||
#include "network/beast_NamedPipe.h"
|
||||
#include "network/beast_Socket.h"
|
||||
#include "network/beast_URL.h"
|
||||
#include "streams/beast_BufferedInputStream.h"
|
||||
#include "streams/beast_FileInputSource.h"
|
||||
#include "streams/beast_InputSource.h"
|
||||
#include "streams/beast_InputStream.h"
|
||||
#include "streams/beast_MemoryInputStream.h"
|
||||
#include "streams/beast_MemoryOutputStream.h"
|
||||
#include "streams/beast_OutputStream.h"
|
||||
#include "streams/beast_SubregionStream.h"
|
||||
|
||||
#include "system/beast_SystemStats.h"
|
||||
#include "text/beast_Identifier.h"
|
||||
#include "text/beast_LocalisedStrings.h"
|
||||
#include "text/beast_NewLine.h"
|
||||
#include "text/beast_StringArray.h"
|
||||
#include "diagnostic/beast_SemanticVersion.h"
|
||||
#include "text/beast_StringPairArray.h"
|
||||
#include "text/beast_StringPool.h"
|
||||
#include "text/beast_TextDiff.h"
|
||||
#include "threads/beast_ChildProcess.h"
|
||||
#include "threads/beast_DynamicLibrary.h"
|
||||
#include "threads/beast_HighResolutionTimer.h"
|
||||
#include "threads/beast_InterProcessLock.h"
|
||||
#include "threads/beast_Process.h"
|
||||
#include "threads/beast_ReadWriteLock.h"
|
||||
#include "threads/beast_ScopedLock.h"
|
||||
#include "threads/beast_ScopedReadLock.h"
|
||||
#include "threads/beast_ScopedWriteLock.h"
|
||||
#include "threads/beast_ThreadLocalValue.h"
|
||||
#include "threads/beast_ThreadPool.h"
|
||||
#include "threads/beast_TimeSliceThread.h"
|
||||
#include "threads/beast_WaitableEvent.h"
|
||||
#include "time/beast_PerformanceCounter.h"
|
||||
#include "time/beast_RelativeTime.h"
|
||||
#include "time/beast_Time.h"
|
||||
#include "diagnostic/beast_UnitTest.h"
|
||||
#include "xml/beast_XmlDocument.h"
|
||||
#include "xml/beast_XmlElement.h"
|
||||
#include "diagnostic/beast_UnitTestUtilities.h"
|
||||
#include "zip/beast_GZIPCompressorOutputStream.h"
|
||||
#include "zip/beast_GZIPDecompressorInputStream.h"
|
||||
#include "zip/beast_ZipFile.h"
|
||||
|
||||
}
|
||||
|
||||
#ifdef _CRTDBG_MAP_ALLOC
|
||||
#pragma pop_macro("_aligned_msize")
|
||||
#pragma pop_macro("_aligned_offset_recalloc")
|
||||
#pragma pop_macro("_aligned_offset_realloc")
|
||||
#pragma pop_macro("_aligned_recalloc")
|
||||
#pragma pop_macro("_aligned_realloc")
|
||||
#pragma pop_macro("_aligned_offset_malloc")
|
||||
#pragma pop_macro("_aligned_malloc")
|
||||
#pragma pop_macro("_aligned_free")
|
||||
#pragma pop_macro("_recalloc")
|
||||
#pragma pop_macro("realloc")
|
||||
#pragma pop_macro("malloc")
|
||||
#pragma pop_macro("free")
|
||||
#pragma pop_macro("calloc")
|
||||
#endif
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AbstractFifo::AbstractFifo (const int capacity) noexcept
|
||||
: bufferSize (capacity)
|
||||
{
|
||||
bassert (bufferSize > 0);
|
||||
}
|
||||
|
||||
AbstractFifo::~AbstractFifo() {}
|
||||
|
||||
int AbstractFifo::getTotalSize() const noexcept { return bufferSize; }
|
||||
int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady(); }
|
||||
|
||||
int AbstractFifo::getNumReady() const noexcept
|
||||
{
|
||||
const int vs = validStart.get();
|
||||
const int ve = validEnd.get();
|
||||
return ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
}
|
||||
|
||||
void AbstractFifo::reset() noexcept
|
||||
{
|
||||
validEnd = 0;
|
||||
validStart = 0;
|
||||
}
|
||||
|
||||
void AbstractFifo::setTotalSize (int newSize) noexcept
|
||||
{
|
||||
bassert (newSize > 0);
|
||||
reset();
|
||||
bufferSize = newSize;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
const int vs = validStart.get();
|
||||
const int ve = validEnd.value;
|
||||
|
||||
const int freeSpace = ve >= vs ? (bufferSize - (ve - vs)) : (vs - ve);
|
||||
numToWrite = bmin (numToWrite, freeSpace - 1);
|
||||
|
||||
if (numToWrite <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = ve;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = bmin (bufferSize - ve, numToWrite);
|
||||
numToWrite -= blockSize1;
|
||||
blockSize2 = numToWrite <= 0 ? 0 : bmin (numToWrite, vs);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedWrite (int numWritten) noexcept
|
||||
{
|
||||
bassert (numWritten >= 0 && numWritten < bufferSize);
|
||||
int newEnd = validEnd.value + numWritten;
|
||||
if (newEnd >= bufferSize)
|
||||
newEnd -= bufferSize;
|
||||
|
||||
validEnd = newEnd;
|
||||
}
|
||||
|
||||
void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept
|
||||
{
|
||||
const int vs = validStart.value;
|
||||
const int ve = validEnd.get();
|
||||
|
||||
const int numReady = ve >= vs ? (ve - vs) : (bufferSize - (vs - ve));
|
||||
numWanted = bmin (numWanted, numReady);
|
||||
|
||||
if (numWanted <= 0)
|
||||
{
|
||||
startIndex1 = 0;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = 0;
|
||||
blockSize2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex1 = vs;
|
||||
startIndex2 = 0;
|
||||
blockSize1 = bmin (bufferSize - vs, numWanted);
|
||||
numWanted -= blockSize1;
|
||||
blockSize2 = numWanted <= 0 ? 0 : bmin (numWanted, ve);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractFifo::finishedRead (int numRead) noexcept
|
||||
{
|
||||
bassert (numRead >= 0 && numRead <= bufferSize);
|
||||
|
||||
int newStart = validStart.value + numRead;
|
||||
if (newStart >= bufferSize)
|
||||
newStart -= bufferSize;
|
||||
|
||||
validStart = newStart;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class AbstractFifoTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AbstractFifoTests() : UnitTest ("Abstract Fifo", "beast")
|
||||
{
|
||||
}
|
||||
|
||||
class WriteThread : public Thread
|
||||
{
|
||||
public:
|
||||
WriteThread (AbstractFifo& fifo_, int* buffer_)
|
||||
: Thread ("fifo writer"), fifo (fifo_), buffer (buffer_)
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
|
||||
~WriteThread()
|
||||
{
|
||||
stopThread (5000);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
int n = 0;
|
||||
Random r;
|
||||
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
int num = r.nextInt (2000) + 1;
|
||||
|
||||
int start1, size1, start2, size2;
|
||||
fifo.prepareToWrite (num, start1, size1, start2, size2);
|
||||
|
||||
bassert (size1 >= 0 && size2 >= 0);
|
||||
bassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()));
|
||||
bassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize()));
|
||||
|
||||
for (int i = 0; i < size1; ++i)
|
||||
buffer [start1 + i] = n++;
|
||||
|
||||
for (int i = 0; i < size2; ++i)
|
||||
buffer [start2 + i] = n++;
|
||||
|
||||
fifo.finishedWrite (size1 + size2);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo& fifo;
|
||||
int* buffer;
|
||||
};
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTestCase ("AbstractFifo");
|
||||
|
||||
int buffer [5000];
|
||||
AbstractFifo fifo (numElementsInArray (buffer));
|
||||
|
||||
WriteThread writer (fifo, buffer);
|
||||
|
||||
int n = 0;
|
||||
Random r;
|
||||
|
||||
bool failed = false;
|
||||
|
||||
for (int count = 100000; --count >= 0;)
|
||||
{
|
||||
int num = r.nextInt (6000) + 1;
|
||||
|
||||
int start1, size1, start2, size2;
|
||||
fifo.prepareToRead (num, start1, size1, start2, size2);
|
||||
|
||||
if (! (size1 >= 0 && size2 >= 0)
|
||||
&& (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize()))
|
||||
&& (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())))
|
||||
{
|
||||
expect (false, "prepareToRead returned negative values");
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size1; ++i)
|
||||
failed = (buffer [start1 + i] != n++) || failed;
|
||||
|
||||
for (int i = 0; i < size2; ++i)
|
||||
failed = (buffer [start2 + i] != n++) || failed;
|
||||
|
||||
if (failed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fifo.finishedRead (size1 + size2);
|
||||
}
|
||||
|
||||
expect (! failed, "read values were incorrect");
|
||||
}
|
||||
};
|
||||
|
||||
static AbstractFifoTests abstractFifoTests;
|
||||
@@ -1,212 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ABSTRACTFIFO_BEASTHEADER
|
||||
#define BEAST_ABSTRACTFIFO_BEASTHEADER
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Encapsulates the logic required to implement a lock-free FIFO.
|
||||
|
||||
This class handles the logic needed when building a single-reader,
|
||||
single-writer FIFO.
|
||||
|
||||
It doesn't actually hold any data itself, but your FIFO class can use one of
|
||||
these to manage its position and status when reading or writing to it.
|
||||
|
||||
To use it, you can call prepareToWrite() to determine the position within
|
||||
your own buffer that an incoming block of data should be stored, and
|
||||
prepareToRead() to find out when the next outgoing block should be read from.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
class MyFifo
|
||||
{
|
||||
public:
|
||||
MyFifo() : abstractFifo (1024)
|
||||
{
|
||||
}
|
||||
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
abstractFifo.finishedWrite (size1 + size2);
|
||||
}
|
||||
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
abstractFifo.prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
abstractFifo.finishedRead (size1 + size2);
|
||||
}
|
||||
|
||||
private:
|
||||
AbstractFifo abstractFifo;
|
||||
int myBuffer [1024];
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
class BEAST_API AbstractFifo : LeakChecked <AbstractFifo>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a FIFO to manage a buffer with the specified capacity. */
|
||||
AbstractFifo (int capacity) noexcept;
|
||||
|
||||
/** Destructor */
|
||||
~AbstractFifo();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the total size of the buffer being managed. */
|
||||
int getTotalSize() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be added to the buffer without it overflowing. */
|
||||
int getFreeSpace() const noexcept;
|
||||
|
||||
/** Returns the number of items that can currently be read from the buffer. */
|
||||
int getNumReady() const noexcept;
|
||||
|
||||
/** Clears the buffer positions, so that it appears empty. */
|
||||
void reset() noexcept;
|
||||
|
||||
/** Changes the buffer's total size.
|
||||
Note that this isn't thread-safe, so don't call it if there's any danger that it
|
||||
might overlap with a call to any other method in this class!
|
||||
*/
|
||||
void setTotalSize (int newSize) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the location within the buffer at which an incoming block of data should be written.
|
||||
|
||||
Because the section of data that you want to add to the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should copy your data into the first one, with any remaining data spilling over into
|
||||
the second.
|
||||
|
||||
If the number of items you ask for is too large to fit within the buffer's free space, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough space available.
|
||||
|
||||
After calling this method, if you choose to write your data into the blocks returned, you
|
||||
must call finishedWrite() to tell the FIFO how much data you actually added.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void addToFifo (const int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToWrite (numItems, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (myBuffer + start1, someData, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (myBuffer + start2, someData + size1, size2);
|
||||
|
||||
finishedWrite (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numToWrite indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedWrite
|
||||
*/
|
||||
void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after writing from the FIFO, to indicate that this many items have been added.
|
||||
@see prepareToWrite
|
||||
*/
|
||||
void finishedWrite (int numWritten) noexcept;
|
||||
|
||||
/** Returns the location within the buffer from which the next block of data should be read.
|
||||
|
||||
Because the section of data that you want to read from the buffer may overlap the end
|
||||
and wrap around to the start, two blocks within your buffer are returned, and you
|
||||
should read from both of them.
|
||||
|
||||
If the number of items you ask for is greater than the amount of data available, then
|
||||
blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you
|
||||
may decide to keep waiting and re-trying the method until there's enough data available.
|
||||
|
||||
After calling this method, if you choose to read the data, you must call finishedRead() to
|
||||
tell the FIFO how much data you have consumed.
|
||||
|
||||
e.g.
|
||||
@code
|
||||
void readFromFifo (int* someData, int numItems)
|
||||
{
|
||||
int start1, size1, start2, size2;
|
||||
prepareToRead (numSamples, start1, size1, start2, size2);
|
||||
|
||||
if (size1 > 0)
|
||||
copySomeData (someData, myBuffer + start1, size1);
|
||||
|
||||
if (size2 > 0)
|
||||
copySomeData (someData + size1, myBuffer + start2, size2);
|
||||
|
||||
finishedRead (size1 + size2);
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param numWanted indicates how many items you'd like to add to the buffer
|
||||
@param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
|
||||
@param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
|
||||
@param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
|
||||
the first block should be written
|
||||
@param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
|
||||
@see finishedRead
|
||||
*/
|
||||
void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
|
||||
|
||||
/** Called after reading from the FIFO, to indicate that this many items have now been consumed.
|
||||
@see prepareToRead
|
||||
*/
|
||||
void finishedRead (int numRead) noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int bufferSize;
|
||||
Atomic <int> validStart, validEnd;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_HASHMAP_BEASTHEADER
|
||||
#define BEAST_HASHMAP_BEASTHEADER
|
||||
|
||||
#include "beast_OwnedArray.h"
|
||||
#include "beast_LinkedListPointer.h"
|
||||
#include "../memory/beast_ScopedPointer.h"
|
||||
|
||||
/** Simple hash functions for use with HashMap.
|
||||
|
||||
@see HashMap
|
||||
*/
|
||||
// VFALCO TODO Rewrite the hash functions to return a uint32, and not
|
||||
// take the upperLimit parameter. Just do the mod in the
|
||||
// calling function for simplicity.
|
||||
class DefaultHashFunctions
|
||||
{
|
||||
public:
|
||||
/** Generates a simple hash from an integer. */
|
||||
int generateHash (const int key, const int upperLimit) const noexcept { return std::abs (key) % upperLimit; }
|
||||
/** Generates a simple hash from an int64. */
|
||||
int generateHash (const int64 key, const int upperLimit) const noexcept { return std::abs ((int) key) % upperLimit; }
|
||||
/** Generates a simple hash from a string. */
|
||||
int generateHash (const String& key, const int upperLimit) const noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); }
|
||||
/** Generates a simple hash from a variant. */
|
||||
int generateHash (const var& key, const int upperLimit) const noexcept { return generateHash (key.toString(), upperLimit); }
|
||||
};
|
||||
|
||||
#if 0
|
||||
/** Hardened hash functions for use with HashMap.
|
||||
|
||||
The seed is used to make the hash unpredictable. This prevents
|
||||
attackers from exploiting crafted inputs to produce degenerate
|
||||
containers.
|
||||
*/
|
||||
class HardenedHashFunctions
|
||||
{
|
||||
public:
|
||||
/** Construct a hash function.
|
||||
|
||||
If a seed is specified it will be used, else a random seed
|
||||
will be generated from the system.
|
||||
|
||||
@param seedToUse An optional seed to use.
|
||||
*/
|
||||
explicit HardenedHashFunctions (int seedToUse = Random::getSystemRandom ().nextInt ())
|
||||
: m_seed (seedToUse)
|
||||
{
|
||||
}
|
||||
|
||||
// VFALCO TODO Need hardened versions of these functions which use the seed!
|
||||
|
||||
private:
|
||||
int m_seed;
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of mappings between some key/value pairs.
|
||||
|
||||
The types of the key and value objects are set as template parameters.
|
||||
You can also specify a class to supply a hash function that converts a key value
|
||||
into an hashed integer. This class must have the form:
|
||||
|
||||
@code
|
||||
struct MyHashGenerator
|
||||
{
|
||||
int generateHash (MyKeyType key, int upperLimit)
|
||||
{
|
||||
// The function must return a value 0 <= x < upperLimit
|
||||
return someFunctionOfMyKeyType (key) % upperLimit;
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
Like the Array class, the key and value types are expected to be copy-by-value
|
||||
types, so if you define them to be pointer types, this class won't delete the
|
||||
objects that they point to.
|
||||
|
||||
If you don't supply a class for the HashFunctionToUse template parameter, the
|
||||
default one provides some simple mappings for strings and ints.
|
||||
|
||||
@code
|
||||
HashMap<int, String> hash;
|
||||
hash.set (1, "item1");
|
||||
hash.set (2, "item2");
|
||||
|
||||
DBG (hash [1]); // prints "item1"
|
||||
DBG (hash [2]); // prints "item2"
|
||||
|
||||
// This iterates the map, printing all of its key -> value pairs..
|
||||
for (HashMap<int, String>::Iterator i (hash); i.next();)
|
||||
DBG (i.getKey() << " -> " << i.getValue());
|
||||
@endcode
|
||||
|
||||
@tparam HashFunctionToUse The type of hash functions, which must be copy
|
||||
constructible.
|
||||
|
||||
@see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet
|
||||
*/
|
||||
template <typename KeyType,
|
||||
typename ValueType,
|
||||
class HashFunctionToUse = DefaultHashFunctions,
|
||||
class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class HashMap
|
||||
: public Uncopyable
|
||||
, LeakChecked <HashMap <KeyType,
|
||||
ValueType,
|
||||
HashFunctionToUse,
|
||||
TypeOfCriticalSectionToUse> >
|
||||
{
|
||||
private:
|
||||
typedef PARAMETER_TYPE (KeyType) KeyTypeParameter;
|
||||
typedef PARAMETER_TYPE (ValueType) ValueTypeParameter;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty hash-map.
|
||||
|
||||
The numberOfSlots parameter specifies the number of hash entries the map will
|
||||
use. This will be the "upperLimit" parameter that is passed to your generateHash()
|
||||
function. The number of hash slots will grow automatically if necessary, or
|
||||
it can be remapped manually using remapTable().
|
||||
|
||||
@param hashFunctionToUse An instance of HashFunctionToUse, which will be
|
||||
copied and stored to use with the HashMap. This
|
||||
can be left out if HashFunctionToUse has a default
|
||||
constructor.
|
||||
*/
|
||||
explicit HashMap (const int numberOfSlots = defaultHashTableSize,
|
||||
HashFunctionToUse hashFunctionToUse_ = HashFunctionToUse ())
|
||||
: hashFunctionToUse (hashFunctionToUse_)
|
||||
, totalNumItems (0)
|
||||
{
|
||||
slots.insertMultiple (0, nullptr, numberOfSlots);
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~HashMap()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all values from the map.
|
||||
Note that this will clear the content, but won't affect the number of slots (see
|
||||
remapTable and getNumSlots).
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (int i = slots.size(); --i >= 0;)
|
||||
{
|
||||
HashEntry* h = slots.getUnchecked(i);
|
||||
|
||||
while (h != nullptr)
|
||||
{
|
||||
const ScopedPointer<HashEntry> deleter (h);
|
||||
h = h->nextEntry;
|
||||
}
|
||||
|
||||
slots.set (i, nullptr);
|
||||
}
|
||||
|
||||
totalNumItems = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current number of items in the map. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return totalNumItems;
|
||||
}
|
||||
|
||||
/** Returns the value corresponding to a given key.
|
||||
If the map doesn't contain the key, a default instance of the value type is returned.
|
||||
@param keyToLookFor the key of the item being requested
|
||||
*/
|
||||
inline ValueType operator[] (KeyTypeParameter keyToLookFor) const
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry)
|
||||
if (entry->key == keyToLookFor)
|
||||
return entry->value;
|
||||
|
||||
return ValueType();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the map contains an item with the specied key. */
|
||||
bool contains (KeyTypeParameter keyToLookFor) const
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry)
|
||||
if (entry->key == keyToLookFor)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if the hash contains at least one occurrence of a given value. */
|
||||
bool containsValue (ValueTypeParameter valueToLookFor) const
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (int i = getNumSlots(); --i >= 0;)
|
||||
for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
|
||||
if (entry->value == valueToLookFor)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds or replaces an element in the hash-map.
|
||||
If there's already an item with the given key, this will replace its value. Otherwise, a new item
|
||||
will be added to the map.
|
||||
*/
|
||||
void set (KeyTypeParameter newKey, ValueTypeParameter newValue)
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
const int hashIndex = generateHashFor (newKey);
|
||||
|
||||
HashEntry* const firstEntry = slots.getUnchecked (hashIndex);
|
||||
|
||||
for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry)
|
||||
{
|
||||
if (entry->key == newKey)
|
||||
{
|
||||
entry->value = newValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry));
|
||||
++totalNumItems;
|
||||
|
||||
if (totalNumItems > (getNumSlots() * 3) / 2)
|
||||
remapTable (getNumSlots() * 2);
|
||||
}
|
||||
|
||||
/** Removes an item with the given key. */
|
||||
void remove (KeyTypeParameter keyToRemove)
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
const int hashIndex = generateHashFor (keyToRemove);
|
||||
HashEntry* entry = slots.getUnchecked (hashIndex);
|
||||
HashEntry* previous = nullptr;
|
||||
|
||||
while (entry != nullptr)
|
||||
{
|
||||
if (entry->key == keyToRemove)
|
||||
{
|
||||
const ScopedPointer<HashEntry> deleter (entry);
|
||||
|
||||
entry = entry->nextEntry;
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->nextEntry = entry;
|
||||
else
|
||||
slots.set (hashIndex, entry);
|
||||
|
||||
--totalNumItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous = entry;
|
||||
entry = entry->nextEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes all items with the given value. */
|
||||
void removeValue (ValueTypeParameter valueToRemove)
|
||||
{
|
||||
const ScopedLockType sl (getLock());
|
||||
|
||||
for (int i = getNumSlots(); --i >= 0;)
|
||||
{
|
||||
HashEntry* entry = slots.getUnchecked(i);
|
||||
HashEntry* previous = nullptr;
|
||||
|
||||
while (entry != nullptr)
|
||||
{
|
||||
if (entry->value == valueToRemove)
|
||||
{
|
||||
const ScopedPointer<HashEntry> deleter (entry);
|
||||
|
||||
entry = entry->nextEntry;
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->nextEntry = entry;
|
||||
else
|
||||
slots.set (i, entry);
|
||||
|
||||
--totalNumItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
previous = entry;
|
||||
entry = entry->nextEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Remaps the hash-map to use a different number of slots for its hash function.
|
||||
Each slot corresponds to a single hash-code, and each one can contain multiple items.
|
||||
@see getNumSlots()
|
||||
*/
|
||||
void remapTable (int newNumberOfSlots)
|
||||
{
|
||||
HashMap newTable (newNumberOfSlots);
|
||||
|
||||
for (int i = getNumSlots(); --i >= 0;)
|
||||
for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
|
||||
newTable.set (entry->key, entry->value);
|
||||
|
||||
swapWith (newTable);
|
||||
}
|
||||
|
||||
/** Returns the number of slots which are available for hashing.
|
||||
Each slot corresponds to a single hash-code, and each one can contain multiple items.
|
||||
@see getNumSlots()
|
||||
*/
|
||||
inline int getNumSlots() const noexcept
|
||||
{
|
||||
return slots.size();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Efficiently swaps the contents of two hash-maps. */
|
||||
template <class OtherHashMapType>
|
||||
void swapWith (OtherHashMapType& otherHashMap) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherHashMapType::ScopedLockType lock2 (otherHashMap.getLock());
|
||||
|
||||
slots.swapWith (otherHashMap.slots);
|
||||
std::swap (totalNumItems, otherHashMap.totalNumItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this structure.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return lock; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class HashEntry : public Uncopyable
|
||||
{
|
||||
public:
|
||||
HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next)
|
||||
: key (k), value (val), nextEntry (next)
|
||||
{}
|
||||
|
||||
const KeyType key;
|
||||
ValueType value;
|
||||
HashEntry* nextEntry;
|
||||
};
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Iterates over the items in a HashMap.
|
||||
|
||||
To use it, repeatedly call next() until it returns false, e.g.
|
||||
@code
|
||||
HashMap <String, String> myMap;
|
||||
|
||||
HashMap<String, String>::Iterator i (myMap);
|
||||
|
||||
while (i.next())
|
||||
{
|
||||
DBG (i.getKey() << " -> " << i.getValue());
|
||||
}
|
||||
@endcode
|
||||
|
||||
The order in which items are iterated bears no resemblence to the order in which
|
||||
they were originally added!
|
||||
|
||||
Obviously as soon as you call any non-const methods on the original hash-map, any
|
||||
iterators that were created beforehand will cease to be valid, and should not be used.
|
||||
|
||||
@see HashMap
|
||||
*/
|
||||
class Iterator : LeakChecked <Iterator>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Iterator (const HashMap& hashMapToIterate)
|
||||
: hashMap (hashMapToIterate), entry (nullptr), index (0)
|
||||
{}
|
||||
|
||||
/** Moves to the next item, if one is available.
|
||||
When this returns true, you can get the item's key and value using getKey() and
|
||||
getValue(). If it returns false, the iteration has finished and you should stop.
|
||||
*/
|
||||
bool next()
|
||||
{
|
||||
if (entry != nullptr)
|
||||
entry = entry->nextEntry;
|
||||
|
||||
while (entry == nullptr)
|
||||
{
|
||||
if (index >= hashMap.getNumSlots())
|
||||
return false;
|
||||
|
||||
entry = hashMap.slots.getUnchecked (index++);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns the current item's key.
|
||||
This should only be called when a call to next() has just returned true.
|
||||
*/
|
||||
KeyType getKey() const
|
||||
{
|
||||
return entry != nullptr ? entry->key : KeyType();
|
||||
}
|
||||
|
||||
/** Returns the current item's value.
|
||||
This should only be called when a call to next() has just returned true.
|
||||
*/
|
||||
ValueType getValue() const
|
||||
{
|
||||
return entry != nullptr ? entry->value : ValueType();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const HashMap& hashMap;
|
||||
HashEntry* entry;
|
||||
int index;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
enum { defaultHashTableSize = 101 };
|
||||
friend class Iterator;
|
||||
|
||||
HashFunctionToUse const hashFunctionToUse;
|
||||
Array <HashEntry*> slots;
|
||||
int totalNumItems;
|
||||
TypeOfCriticalSectionToUse lock;
|
||||
|
||||
int generateHashFor (KeyTypeParameter key) const
|
||||
{
|
||||
const int hash = hashFunctionToUse.generateHash (key, getNumSlots());
|
||||
bassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers!
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_HASHMAP_BEASTHEADER
|
||||
@@ -1,799 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_LIST_H_INCLUDED
|
||||
#define BEAST_LIST_H_INCLUDED
|
||||
|
||||
/** Intrusive Containers
|
||||
|
||||
# Introduction
|
||||
|
||||
Intrusive containers are special containers that offer better performance
|
||||
and exception safety guarantees than non-intrusive containers (like the
|
||||
STL containers). They are useful building blocks for high performance
|
||||
concurrent systems or other purposes where allocations are restricted
|
||||
(such as the AudioIODeviceCallback object), because intrusive list
|
||||
operations do not allocate or free memory.
|
||||
|
||||
While intrusive containers were and are widely used in C, they became more
|
||||
and more forgotten in C++ due to the presence of the standard containers
|
||||
which don't support intrusive techniques. VFLib not only reintroduces this
|
||||
technique to C++ for lists, it also encapsulates the implementation in a
|
||||
mostly compliant STL interface. Hence anyone familiar with standard
|
||||
containers can easily use them.
|
||||
|
||||
# Interface
|
||||
|
||||
The interface for intrusive elements in this library is unified for all
|
||||
containers. Unlike STL containers, objects placed into intrusive containers
|
||||
are not copied. Instead, a pointer to the object is stored. All
|
||||
responsibility for object lifetime is the responsibility of the caller;
|
||||
the intrusive container just keeps track of what is in it.
|
||||
|
||||
Summary of intrusive container differences:
|
||||
|
||||
- Holds pointers to existing objects instead of copies.
|
||||
|
||||
- Does not allocate or free any objects.
|
||||
|
||||
- Requires a element's class declaration to be modified.
|
||||
|
||||
- Methods never throw exceptions when called with valid arguments.
|
||||
|
||||
# Usage
|
||||
|
||||
Like STL containers, intrusive containers are all template based, where the
|
||||
template argument specifies the type of object that the container will hold.
|
||||
These declarations specify a doubly linked list where each element points
|
||||
to a user defined class:
|
||||
|
||||
@code
|
||||
|
||||
struct Object; // Forward declaration
|
||||
|
||||
List <Object> list; // Doubly-linked list of Object
|
||||
|
||||
@endcode
|
||||
|
||||
Because intrusive containers allocate no memory, allowing objects to be
|
||||
placed inside requires a modification to their class declaration. Each
|
||||
intrusive container declares a nested class `Node` which elements must be
|
||||
derived from, using the Curiously Recurring Template Pattern (CRTP). We
|
||||
will continue to fully declare the Object type from the previous example
|
||||
to support emplacement into an intrusive container:
|
||||
|
||||
@code
|
||||
|
||||
struct Object : public List <Object>::Node // Required for List
|
||||
{
|
||||
void performAction ();
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
Usage of a typedef eliminates redundant specification of the template
|
||||
arguments but requires a forward declaration. The following code is
|
||||
equivalent.
|
||||
|
||||
@code
|
||||
|
||||
struct Object; // Forward declaration
|
||||
|
||||
// Specify template parameters just once
|
||||
typedef List <Object> ListType;
|
||||
|
||||
struct Object : public ListType::Node
|
||||
{
|
||||
void performAction ();
|
||||
};
|
||||
|
||||
ListType::Node list;
|
||||
|
||||
@endcode
|
||||
|
||||
With these declarations we may proceed to create our objects, add them to
|
||||
the list, and perform operations:
|
||||
|
||||
@code
|
||||
|
||||
// Create a few objects and put them in the list
|
||||
for (i = 0; i < 5; ++i)
|
||||
list.push_back (*new Object);
|
||||
|
||||
// Call a method on each list
|
||||
for (ListType::iterator iter = list.begin(); iter != list.end (); ++iter)
|
||||
iter->performAction ();
|
||||
|
||||
@endcode
|
||||
|
||||
Unlike regular STL containers, an object derived from an intrusive container
|
||||
node cannot exist in more than one instance of that list at a time. This is
|
||||
because the bookkeeping information for maintaining the list is kept in
|
||||
the object rather than the list.
|
||||
|
||||
To support objects existing in multiple containers, templates variations
|
||||
are instantiated by distinguishing them with an empty structure, called a
|
||||
tag. The object is derived from multiple instances of Node, where each
|
||||
instance specifies a unique tag. The tag is passed as the second template
|
||||
argument. When the second argument is unspecified, the default tag is used.
|
||||
|
||||
This declaration example shows the usage of tags to allow an object to exist
|
||||
simultaneously in two separate lists:
|
||||
|
||||
@code
|
||||
|
||||
struct GlobalListTag { }; // list of all objects
|
||||
struct ActiveListTag { }; // subset of all objects that are active
|
||||
|
||||
class Object : public List <Object, GlobalListTag>
|
||||
, public List <Object, ActiveListTag>
|
||||
{
|
||||
public:
|
||||
Object () : m_isActive (false)
|
||||
{
|
||||
// Add ourselves to the global list
|
||||
s_globalList.push_front (*this);
|
||||
}
|
||||
|
||||
~Object ()
|
||||
{
|
||||
deactivate ();
|
||||
}
|
||||
|
||||
void becomeActive ()
|
||||
{
|
||||
// Add ourselves to the active list
|
||||
if (!m_isActive)
|
||||
{
|
||||
s_activeList.push_front (*this);
|
||||
m_isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void deactivate ()
|
||||
{
|
||||
if (m_isActive)
|
||||
{
|
||||
// Doesn't delete the object
|
||||
s_activeList.erase (s_activeList.iterator_to (this));
|
||||
|
||||
m_isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isActive;
|
||||
|
||||
static List <Object, GlobalListTag> s_globalList;
|
||||
static List <Object, ActiveListTag> s_activeList;
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@defgroup intrusive intrusive
|
||||
@ingroup beast_core
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Default tag for List.
|
||||
|
||||
@ingroup beast_core intrusive
|
||||
*/
|
||||
struct ListDefaultTag;
|
||||
|
||||
/**
|
||||
Intrusive doubly linked list.
|
||||
|
||||
This intrusive List is a container similar in operation to std::list in the
|
||||
Standard Template Library (STL). Like all @ref intrusive containers, List
|
||||
requires you to first derive your class from List<>::Node:
|
||||
|
||||
@code
|
||||
|
||||
struct Object : List <Object>::Node
|
||||
{
|
||||
explicit Object (int value) : m_value (value)
|
||||
{
|
||||
}
|
||||
|
||||
int m_value;
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
Now we define the list, and add a couple of items.
|
||||
|
||||
@code
|
||||
|
||||
List <Object> list;
|
||||
|
||||
list.push_back (* (new Object (1)));
|
||||
list.push_back (* (new Object (2)));
|
||||
|
||||
@endcode
|
||||
|
||||
For compatibility with the standard containers, push_back() expects a
|
||||
reference to the object. Unlike the standard container, however, push_back()
|
||||
places the actual object in the list and not a copy-constructed duplicate.
|
||||
|
||||
Iterating over the list follows the same idiom as the STL:
|
||||
|
||||
@code
|
||||
|
||||
for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter)
|
||||
std::cout << iter->m_value;
|
||||
|
||||
@endcode
|
||||
|
||||
You can even use BOOST_FOREACH, or range based for loops:
|
||||
|
||||
@code
|
||||
|
||||
BOOST_FOREACH (Object& object, list) // boost only
|
||||
std::cout << object.m_value;
|
||||
|
||||
for (Object& object : list) // C++11 only
|
||||
std::cout << object.m_value;
|
||||
|
||||
@endcode
|
||||
|
||||
Because List is mostly STL compliant, it can be passed into STL algorithms:
|
||||
e.g. `std::for_each()` or `std::find_first_of()`.
|
||||
|
||||
In general, objects placed into a List should be dynamically allocated
|
||||
although this cannot be enforced at compile time. Since the caller provides
|
||||
the storage for the object, the caller is also responsible for deleting the
|
||||
object. An object still exists after being removed from a List, until the
|
||||
caller deletes it. This means an element can be moved from one List to
|
||||
another with practically no overhead.
|
||||
|
||||
Unlike the standard containers, an object may only exist in one list at a
|
||||
time, unless special preparations are made. The Tag template parameter is
|
||||
used to distinguish between different list types for the same object,
|
||||
allowing the object to exist in more than one list simultaneously.
|
||||
|
||||
For example, consider an actor system where a global list of actors is
|
||||
maintained, so that they can each be periodically receive processing
|
||||
time. We wish to also maintain a list of the subset of actors that require
|
||||
a domain-dependent update. To achieve this, we declare two tags, the
|
||||
associated list types, and the list element thusly:
|
||||
|
||||
@code
|
||||
|
||||
struct Actor; // Forward declaration required
|
||||
|
||||
struct ProcessTag { };
|
||||
struct UpdateTag { };
|
||||
|
||||
typedef List <Actor, ProcessTag> ProcessList;
|
||||
typedef List <Actor, UpdateTag> UpdateList;
|
||||
|
||||
// Derive from both node types so we can be in each list at once.
|
||||
//
|
||||
struct Actor : ProcessList::Node, UpdateList::Node
|
||||
{
|
||||
bool process (); // returns true if we need an update
|
||||
void update ();
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
@tparam Element The base type of element which the list will store
|
||||
pointers to.
|
||||
|
||||
@tparam Tag An optional unique type name used to distinguish lists and nodes,
|
||||
when the object can exist in multiple lists simultaneously.
|
||||
|
||||
@ingroup beast_core intrusive
|
||||
*/
|
||||
template <class Element, class Tag = ListDefaultTag>
|
||||
class List : public Uncopyable
|
||||
{
|
||||
public:
|
||||
typedef int size_type;
|
||||
|
||||
typedef Element value_type;
|
||||
typedef Element& reference;
|
||||
typedef Element const& const_reference;
|
||||
typedef Element* pointer;
|
||||
typedef Element const* const_pointer;
|
||||
|
||||
/** Thrown when some members are called with an empty list. */
|
||||
struct empty_list_error : std::logic_error
|
||||
{
|
||||
empty_list_error () : std::logic_error ("empty list")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Node : public Uncopyable
|
||||
{
|
||||
public:
|
||||
Node () { }
|
||||
|
||||
private:
|
||||
friend class List;
|
||||
Node* m_next;
|
||||
Node* m_prev;
|
||||
};
|
||||
|
||||
private:
|
||||
template <class ElemType, class NodeType>
|
||||
class iterator_base : public std::iterator <
|
||||
std::bidirectional_iterator_tag, int >
|
||||
{
|
||||
public:
|
||||
typedef ElemType value_type;
|
||||
typedef ElemType* pointer;
|
||||
typedef ElemType& reference;
|
||||
|
||||
iterator_base (NodeType* node = nullptr) : m_node (node)
|
||||
{
|
||||
}
|
||||
|
||||
template <class OtherElemType, class OtherNodeType>
|
||||
iterator_base (iterator_base <OtherElemType, OtherNodeType> const& other)
|
||||
: m_node (other.m_node)
|
||||
{
|
||||
}
|
||||
|
||||
template <class OtherElemType, class OtherNodeType>
|
||||
iterator_base& operator= (iterator_base <OtherElemType, OtherNodeType> const& other)
|
||||
{
|
||||
m_node = other.m_node;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class OtherElemType, class OtherNodeType>
|
||||
bool operator == (iterator_base <OtherElemType, OtherNodeType> const& other) const
|
||||
{
|
||||
return m_node == other.m_node;
|
||||
}
|
||||
|
||||
template <class OtherElemType, class OtherNodeType>
|
||||
bool operator != (iterator_base <OtherElemType, OtherNodeType> const& other) const
|
||||
{
|
||||
return ! this->operator== (other);
|
||||
}
|
||||
|
||||
reference operator* () const
|
||||
{
|
||||
return dereference ();
|
||||
}
|
||||
|
||||
pointer operator-> () const
|
||||
{
|
||||
return &dereference ();
|
||||
}
|
||||
|
||||
iterator_base& operator++ ()
|
||||
{
|
||||
increment ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_base operator++ (int)
|
||||
{
|
||||
iterator_base result (*this);
|
||||
increment ();
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator_base& operator-- ()
|
||||
{
|
||||
decrement ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_base operator-- (int)
|
||||
{
|
||||
iterator_base result (*this);
|
||||
decrement ();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class List;
|
||||
|
||||
NodeType* get_node ()
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
NodeType const* get_node () const
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
reference dereference () const
|
||||
{
|
||||
return *static_cast <ElemType*> (m_node);
|
||||
}
|
||||
|
||||
bool equal (NodeType* const* node) const
|
||||
{
|
||||
return m_node == node;
|
||||
}
|
||||
|
||||
void increment ()
|
||||
{
|
||||
bassert (m_node->m_next);
|
||||
m_node = m_node->m_next;
|
||||
}
|
||||
|
||||
void decrement ()
|
||||
{
|
||||
bassert (m_node->m_prev && m_node->m_prev->m_prev != 0);
|
||||
m_node = m_node->m_prev;
|
||||
}
|
||||
|
||||
private:
|
||||
NodeType* m_node;
|
||||
};
|
||||
|
||||
public:
|
||||
/** A read/write List iterator. */
|
||||
typedef iterator_base <Element, Node> iterator;
|
||||
|
||||
/** A read-only List iterator. */
|
||||
typedef iterator_base <Element const, Node const> const_iterator;
|
||||
|
||||
public:
|
||||
/** Create an empty list. */
|
||||
List () : m_size (0)
|
||||
{
|
||||
m_head.m_prev = nullptr; // identifies the head
|
||||
m_tail.m_next = nullptr; // identifies the tail
|
||||
clear ();
|
||||
}
|
||||
|
||||
/** Returns the number of elements in the list
|
||||
|
||||
@return The number of elements in the list.
|
||||
*/
|
||||
size_type size () const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/** Obtain a reference to the first element.
|
||||
|
||||
@invariant The list may not be empty.
|
||||
|
||||
@return A reference to the first element.
|
||||
*/
|
||||
reference front ()
|
||||
{
|
||||
if (empty ())
|
||||
Throw (empty_list_error (), __FILE__, __LINE__);
|
||||
|
||||
return element_from (m_head.m_next);
|
||||
}
|
||||
|
||||
/** Obtain a const reference to the first element.
|
||||
|
||||
@invariant The list may not be empty.
|
||||
|
||||
@return A const reference to the first element.
|
||||
*/
|
||||
const_reference front () const
|
||||
{
|
||||
if (empty ())
|
||||
Throw (empty_list_error (), __FILE__, __LINE__);
|
||||
|
||||
return element_from (m_head.m_next);
|
||||
}
|
||||
|
||||
/** Obtain a reference to the last element.
|
||||
|
||||
@invariant The list may not be empty.
|
||||
|
||||
@return A reference to the last element.
|
||||
*/
|
||||
reference back ()
|
||||
{
|
||||
if (empty ())
|
||||
Throw (empty_list_error (), __FILE__, __LINE__);
|
||||
|
||||
return element_from (m_tail.m_prev);
|
||||
}
|
||||
|
||||
/** Obtain a const reference to the last element.
|
||||
|
||||
@invariant The list may not be empty.
|
||||
|
||||
@return A const reference to the last element.
|
||||
*/
|
||||
const_reference back () const
|
||||
{
|
||||
if (empty ())
|
||||
Throw (empty_list_error (), __FILE__, __LINE__);
|
||||
|
||||
return element_from (m_tail.m_prev);
|
||||
}
|
||||
|
||||
/** Obtain an iterator to the beginning of the list.
|
||||
|
||||
@return An iterator pointing to the beginning of the list.
|
||||
*/
|
||||
iterator begin ()
|
||||
{
|
||||
return iterator (m_head.m_next);
|
||||
}
|
||||
|
||||
/** Obtain a const iterator to the beginning of the list.
|
||||
|
||||
@return A const iterator pointing to the beginning of the list.
|
||||
*/
|
||||
const_iterator begin () const
|
||||
{
|
||||
return const_iterator (m_head.m_next);
|
||||
}
|
||||
|
||||
/** Obtain a const iterator to the beginning of the list.
|
||||
|
||||
@return A const iterator pointing to the beginning of the list.
|
||||
*/
|
||||
const_iterator cbegin () const
|
||||
{
|
||||
return const_iterator (m_head.m_next);
|
||||
}
|
||||
|
||||
/** Obtain a iterator to the end of the list.
|
||||
|
||||
@return An iterator pointing to the end of the list.
|
||||
*/
|
||||
iterator end ()
|
||||
{
|
||||
return iterator (&m_tail);
|
||||
}
|
||||
|
||||
/** Obtain a const iterator to the end of the list.
|
||||
|
||||
@return A constiterator pointing to the end of the list.
|
||||
*/
|
||||
const_iterator end () const
|
||||
{
|
||||
return const_iterator (&m_tail);
|
||||
}
|
||||
|
||||
/** Obtain a const iterator to the end of the list.
|
||||
|
||||
@return A constiterator pointing to the end of the list.
|
||||
*/
|
||||
const_iterator cend () const
|
||||
{
|
||||
return const_iterator (&m_tail);
|
||||
}
|
||||
|
||||
/** Determine if the list is empty.
|
||||
|
||||
@return `true` if the list is empty.
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_head.m_next == &m_tail;
|
||||
}
|
||||
|
||||
/** Clear the list.
|
||||
|
||||
@note This does not free the elements.
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_head.m_next = &m_tail;
|
||||
m_tail.m_prev = &m_head;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
/** Insert an element.
|
||||
|
||||
@invariant The element must not already be in the list.
|
||||
|
||||
@param pos The location to insert after.
|
||||
|
||||
@param elem The element to insert.
|
||||
|
||||
@return An iterator pointing to the newly inserted element.
|
||||
*/
|
||||
iterator insert (iterator pos, Element& elem)
|
||||
{
|
||||
Node* node = node_from (elem);
|
||||
node->m_next = pos.get_node ();
|
||||
node->m_prev = node->m_next->m_prev;
|
||||
node->m_next->m_prev = node;
|
||||
node->m_prev->m_next = node;
|
||||
++m_size;
|
||||
return iterator (node);
|
||||
}
|
||||
|
||||
/** Insert another list into this one.
|
||||
|
||||
The other list is cleared.
|
||||
|
||||
@param pos The location to insert after.
|
||||
|
||||
@param other The list to insert.
|
||||
*/
|
||||
|
||||
void insert (iterator pos, List& other)
|
||||
{
|
||||
if (!other.empty ())
|
||||
{
|
||||
Node* before = pos.get_node ();
|
||||
other.m_head.m_next->m_prev = before->m_prev;
|
||||
before->m_prev->m_next = other.m_head.m_next;
|
||||
other.m_tail.m_prev->m_next = before;
|
||||
before->m_prev = other.m_tail.m_prev;
|
||||
m_size += other.m_size;
|
||||
other.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove an element.
|
||||
|
||||
@invariant The element must exist in the list.
|
||||
|
||||
@param pos An iterator pointing to the element to remove.
|
||||
|
||||
@return An iterator pointing to the next element after the one removed.
|
||||
*/
|
||||
iterator erase (iterator pos)
|
||||
{
|
||||
Node* node = pos.get_node ();
|
||||
++pos;
|
||||
node->m_next->m_prev = node->m_prev;
|
||||
node->m_prev->m_next = node->m_next;
|
||||
--m_size;
|
||||
return pos;
|
||||
}
|
||||
|
||||
/** Insert an element at the beginning of the list.
|
||||
|
||||
@invariant The element must not exist in the list.
|
||||
|
||||
@param elem The element to insert.
|
||||
*/
|
||||
void push_front (Element& elem)
|
||||
{
|
||||
insert (begin (), elem);
|
||||
}
|
||||
|
||||
/** Remove the element at the beginning of the list.
|
||||
|
||||
@invariant The list must not be empty.
|
||||
@return A reference to the popped element.
|
||||
*/
|
||||
Element& pop_front ()
|
||||
{
|
||||
Element& elem (front ());
|
||||
erase (begin ());
|
||||
return elem;
|
||||
}
|
||||
|
||||
/** Append an element at the end of the list.
|
||||
|
||||
@invariant The element must not exist in the list.
|
||||
|
||||
@param elem The element to append.
|
||||
*/
|
||||
void push_back (Element& elem)
|
||||
{
|
||||
insert (end (), elem);
|
||||
}
|
||||
|
||||
/** Remove the element at the end of the list.
|
||||
|
||||
@invariant The list must not be empty.
|
||||
@return A reference to the popped element.
|
||||
*/
|
||||
Element& pop_back ()
|
||||
{
|
||||
Element& elem (back ());
|
||||
erase (--end ());
|
||||
return elem;
|
||||
}
|
||||
|
||||
/** Swap contents with another list.
|
||||
*/
|
||||
void swap (List& other)
|
||||
{
|
||||
List temp;
|
||||
temp.append (other);
|
||||
other.append (*this);
|
||||
append (temp);
|
||||
}
|
||||
|
||||
/** Insert another list at the beginning of this list.
|
||||
|
||||
The other list is cleared.
|
||||
|
||||
@param list The other list to insert.
|
||||
*/
|
||||
void prepend (List& list)
|
||||
{
|
||||
insert (begin (), list);
|
||||
}
|
||||
|
||||
/** Append another list at the end of this list.
|
||||
|
||||
The other list is cleared.
|
||||
|
||||
@param list the other list to append.
|
||||
*/
|
||||
void append (List& list)
|
||||
{
|
||||
insert (end (), list);
|
||||
}
|
||||
|
||||
/** Obtain an iterator from an element.
|
||||
|
||||
@invariant The element must exist in the list.
|
||||
|
||||
@param elem The element to obtain an iterator for.
|
||||
|
||||
@return An iterator to the element.
|
||||
*/
|
||||
iterator iterator_to (Element& elem) const
|
||||
{
|
||||
return iterator (static_cast <Node*> (&elem));
|
||||
}
|
||||
|
||||
/** Obtain a const iterator from an element.
|
||||
|
||||
@invariant The element must exist in the list.
|
||||
|
||||
@param elem The element to obtain an iterator for.
|
||||
|
||||
@return A const iterator to the element.
|
||||
*/
|
||||
const_iterator const_iterator_to (Element const& elem) const
|
||||
{
|
||||
return const_iterator (static_cast <Node const*> (&elem));
|
||||
}
|
||||
|
||||
private:
|
||||
inline reference element_from (Node* node)
|
||||
{
|
||||
return * (static_cast <pointer> (node));
|
||||
}
|
||||
|
||||
inline const_reference element_from (Node const* node) const
|
||||
{
|
||||
return * (static_cast <const_pointer> (node));
|
||||
}
|
||||
|
||||
inline Node* node_from (Element& elem)
|
||||
{
|
||||
return static_cast <Node*> (&elem);
|
||||
}
|
||||
|
||||
inline Node const* node_from (Element const& elem) const
|
||||
{
|
||||
return static_cast <Node const*> (&elem);
|
||||
}
|
||||
|
||||
private:
|
||||
size_type m_size;
|
||||
Node m_head;
|
||||
Node m_tail;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,165 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_LOCKFREESTACK_BEASTHEADER
|
||||
#define BEAST_LOCKFREESTACK_BEASTHEADER
|
||||
|
||||
struct LockFreeStackDefaultTag;
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
|
||||
|
||||
This stack is implemented using the same intrusive interface as List. All
|
||||
operations are lock-free.
|
||||
|
||||
The caller is responsible for preventing the "ABA" problem
|
||||
(http://en.wikipedia.org/wiki/ABA_problem)
|
||||
|
||||
@param Tag A type name used to distinguish lists and nodes, for
|
||||
putting objects in multiple lists. If this parameter is
|
||||
omitted, the default tag is used.
|
||||
|
||||
@ingroup beast_core intrusive
|
||||
*/
|
||||
template <class Element, class Tag = LockFreeStackDefaultTag>
|
||||
class LockFreeStack : public Uncopyable
|
||||
{
|
||||
public:
|
||||
class Node : public Uncopyable
|
||||
{
|
||||
public:
|
||||
Node ()
|
||||
{
|
||||
}
|
||||
|
||||
explicit Node (Node* next) : m_next (next)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class LockFreeStack;
|
||||
|
||||
// VFALCO TODO Use regular Atomic<>
|
||||
AtomicPointer <Node> m_next;
|
||||
};
|
||||
|
||||
public:
|
||||
LockFreeStack () : m_head (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Create a LockFreeStack from another stack.
|
||||
|
||||
The contents of the other stack are atomically acquired.
|
||||
The other stack is cleared.
|
||||
|
||||
@param other The other stack to acquire.
|
||||
*/
|
||||
explicit LockFreeStack (LockFreeStack& other)
|
||||
{
|
||||
Node* head;
|
||||
|
||||
do
|
||||
{
|
||||
head = other.m_head.get ();
|
||||
}
|
||||
while (!other.m_head.compareAndSet (0, head));
|
||||
|
||||
m_head = head;
|
||||
}
|
||||
|
||||
/** Push a node onto the stack.
|
||||
|
||||
The caller is responsible for preventing the ABA problem. This operation
|
||||
is lock-free.
|
||||
|
||||
@param node The node to push.
|
||||
|
||||
@return True if the stack was previously empty. If multiple threads
|
||||
are attempting to push, only one will receive true.
|
||||
*/
|
||||
bool push_front (Node* node)
|
||||
{
|
||||
bool first;
|
||||
Node* head;
|
||||
|
||||
do
|
||||
{
|
||||
head = m_head.get ();
|
||||
first = head == 0;
|
||||
node->m_next = head;
|
||||
}
|
||||
while (!m_head.compareAndSet (node, head));
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
/** Pop an element off the stack.
|
||||
|
||||
The caller is responsible for preventing the ABA problem. This operation
|
||||
is lock-free.
|
||||
|
||||
@return The element that was popped, or nullptr if the stack was empty.
|
||||
*/
|
||||
Element* pop_front ()
|
||||
{
|
||||
Node* node;
|
||||
Node* head;
|
||||
|
||||
do
|
||||
{
|
||||
node = m_head.get ();
|
||||
|
||||
if (node == 0)
|
||||
break;
|
||||
|
||||
head = node->m_next.get ();
|
||||
}
|
||||
while (!m_head.compareAndSet (head, node));
|
||||
|
||||
return node ? static_cast <Element*> (node) : nullptr;
|
||||
}
|
||||
|
||||
/** Swap the contents of this stack with another stack.
|
||||
|
||||
This call is not thread safe or atomic. The caller is responsible for
|
||||
synchronizing access.
|
||||
|
||||
@param other The other stack to swap contents with.
|
||||
*/
|
||||
void swap (LockFreeStack& other)
|
||||
{
|
||||
Node* temp = other.m_head.get ();
|
||||
other.m_head.set (m_head.get ());
|
||||
m_head.set (temp);
|
||||
}
|
||||
|
||||
private:
|
||||
AtomicPointer <Node> m_head;
|
||||
};
|
||||
|
||||
/*============================================================================*/
|
||||
/** Default tag for LockFreeStack
|
||||
|
||||
@ingroup beast_core intrusive
|
||||
*/
|
||||
struct LockFreeStackDefaultTag { };
|
||||
|
||||
#endif
|
||||
@@ -1,862 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SHAREDOBJECTARRAY_H_INCLUDED
|
||||
#define BEAST_SHAREDOBJECTARRAY_H_INCLUDED
|
||||
|
||||
#include "../memory/beast_SharedObject.h"
|
||||
#include "beast_ArrayAllocationBase.h"
|
||||
#include "beast_ElementComparator.h"
|
||||
#include "../threads/beast_CriticalSection.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a list of objects derived from SharedObject.
|
||||
|
||||
A SharedObjectArray holds objects derived from SharedObject,
|
||||
and takes care of incrementing and decrementing their ref counts when they
|
||||
are added and removed from the array.
|
||||
|
||||
To make all the array's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, StringArray
|
||||
*/
|
||||
template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SharedObjectArray
|
||||
{
|
||||
public:
|
||||
typedef SharedObjectPtr<ObjectClass> ObjectClassPtr;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty array.
|
||||
@see SharedObject, Array, OwnedArray
|
||||
*/
|
||||
SharedObjectArray() noexcept
|
||||
: numUsed (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
SharedObjectArray (const SharedObjectArray& other) noexcept
|
||||
{
|
||||
const ScopedLockType lock (other.getLock());
|
||||
numUsed = other.size();
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Creates a copy of another array */
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
SharedObjectArray (const SharedObjectArray<OtherObjectClass, OtherCriticalSection>& other) noexcept
|
||||
{
|
||||
const typename SharedObjectArray<OtherObjectClass, OtherCriticalSection>::ScopedLockType lock (other.getLock());
|
||||
numUsed = other.size();
|
||||
data.setAllocatedSize (numUsed);
|
||||
memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*));
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Copies another array into this one.
|
||||
Any existing objects in this array will first be released.
|
||||
*/
|
||||
SharedObjectArray& operator= (const SharedObjectArray& other) noexcept
|
||||
{
|
||||
SharedObjectArray otherCopy (other);
|
||||
swapWith (otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copies another array into this one.
|
||||
Any existing objects in this array will first be released.
|
||||
*/
|
||||
template <class OtherObjectClass>
|
||||
SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse>& operator= (const SharedObjectArray<OtherObjectClass, TypeOfCriticalSectionToUse>& other) noexcept
|
||||
{
|
||||
SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse> otherCopy (other);
|
||||
swapWith (otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
Any objects in the array will be released, and may be deleted if not referenced from elsewhere.
|
||||
*/
|
||||
~SharedObjectArray()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all objects from the array.
|
||||
|
||||
Any objects in the array that are not referenced from elsewhere will be deleted.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (numUsed > 0)
|
||||
if (ObjectClass* o = data.elements [--numUsed])
|
||||
o->decReferenceCount();
|
||||
|
||||
bassert (numUsed == 0);
|
||||
data.setAllocatedSize (0);
|
||||
}
|
||||
|
||||
/** Returns the current number of objects in the array. */
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return numUsed;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClassPtr operator[] (const int index) const noexcept
|
||||
{
|
||||
return getObjectPointer (index);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the object at this index in the array, without checking
|
||||
whether the index is in-range.
|
||||
|
||||
This is a faster and less safe version of operator[] which doesn't check the index passed in, so
|
||||
it can be used when you're sure the index is always going to be legal.
|
||||
*/
|
||||
inline ObjectClassPtr getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return getObjectPointerUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array.
|
||||
|
||||
If the index is out-of-range, this will return a null pointer, (and
|
||||
it could be null anyway, because it's ok for the array to hold null
|
||||
pointers as well as objects).
|
||||
|
||||
@see getUnchecked
|
||||
*/
|
||||
inline ObjectClass* getObjectPointer (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
return isPositiveAndBelow (index, numUsed) ? data.elements [index]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
/** Returns a raw pointer to the object at this index in the array, without checking
|
||||
whether the index is in-range.
|
||||
*/
|
||||
inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
bassert (isPositiveAndBelow (index, numUsed));
|
||||
return data.elements [index];
|
||||
}
|
||||
|
||||
/** Returns a pointer to the first object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getLast
|
||||
*/
|
||||
inline ObjectClassPtr getFirst() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
return numUsed > 0 ? data.elements [0]
|
||||
: static_cast <ObjectClass*> (nullptr);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the last object in the array.
|
||||
|
||||
This will return a null pointer if the array's empty.
|
||||
@see getFirst
|
||||
*/
|
||||
inline ObjectClassPtr getLast() const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
return numUsed > 0 ? data.elements [numUsed - 1]
|
||||
: static_cast <ObjectClass*> (nullptr);
|
||||
}
|
||||
|
||||
/** Returns a pointer to the actual array data.
|
||||
This pointer will only be valid until the next time a non-const method
|
||||
is called on the array.
|
||||
*/
|
||||
inline ObjectClass** getRawDataPointer() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** begin() const noexcept
|
||||
{
|
||||
return data.elements;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the array.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ObjectClass** end() const noexcept
|
||||
{
|
||||
return data.elements + numUsed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first occurrence of an object in the array.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns the index at which the object was found, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** e = data.elements.getData();
|
||||
ObjectClass** const endPointer = e + numUsed;
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return static_cast <int> (e - data.elements.getData());
|
||||
|
||||
++e;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Returns true if the array contains a specified object.
|
||||
|
||||
@param objectToLookFor the object to look for
|
||||
@returns true if the object is in the array
|
||||
*/
|
||||
bool contains (const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
ObjectClass** e = data.elements.getData();
|
||||
ObjectClass** const endPointer = e + numUsed;
|
||||
|
||||
while (e != endPointer)
|
||||
{
|
||||
if (objectToLookFor == *e)
|
||||
return true;
|
||||
|
||||
++e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Appends a new object to the end of the array.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addArray
|
||||
*/
|
||||
ObjectClass* add (ObjectClass* const newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
bassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array at the given index.
|
||||
|
||||
If the index is less than 0 or greater than the size of the array, the
|
||||
element will be added to the end of the array.
|
||||
Otherwise, it will be inserted into the array, moving all the later elements
|
||||
along to make room.
|
||||
|
||||
This will increase the new object's reference count.
|
||||
|
||||
@param indexToInsertAt the index at which the new element should be inserted
|
||||
@param newObject the new object to add to the array
|
||||
@see add, addSorted, addIfNotAlreadyThere, set
|
||||
*/
|
||||
ObjectClass* insert (int indexToInsertAt,
|
||||
ObjectClass* const newObject) noexcept
|
||||
{
|
||||
if (indexToInsertAt >= 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (indexToInsertAt > numUsed)
|
||||
indexToInsertAt = numUsed;
|
||||
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
bassert (data.elements != nullptr);
|
||||
|
||||
ObjectClass** const e = data.elements + indexToInsertAt;
|
||||
const int numToMove = numUsed - indexToInsertAt;
|
||||
|
||||
if (numToMove > 0)
|
||||
memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove);
|
||||
|
||||
*e = newObject;
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
++numUsed;
|
||||
|
||||
return newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
return add (newObject);
|
||||
}
|
||||
}
|
||||
|
||||
/** Appends a new object at the end of the array as long as the array doesn't
|
||||
already contain it.
|
||||
|
||||
If the array already contains a matching object, nothing will be done.
|
||||
|
||||
@param newObject the new object to add to the array
|
||||
*/
|
||||
void addIfNotAlreadyThere (ObjectClass* const newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
if (! contains (newObject))
|
||||
add (newObject);
|
||||
}
|
||||
|
||||
/** Replaces an object in the array with a different one.
|
||||
|
||||
If the index is less than zero, this method does nothing.
|
||||
If the index is beyond the end of the array, the new object is added to the end of the array.
|
||||
|
||||
The object being added has its reference count increased, and if it's replacing
|
||||
another object, then that one has its reference count decreased, and may be deleted.
|
||||
|
||||
@param indexToChange the index whose value you want to change
|
||||
@param newObject the new value to set for this index.
|
||||
@see add, insert, remove
|
||||
*/
|
||||
void set (const int indexToChange,
|
||||
ObjectClass* const newObject)
|
||||
{
|
||||
if (indexToChange >= 0)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
if (indexToChange < numUsed)
|
||||
{
|
||||
if (ObjectClass* o = data.elements [indexToChange])
|
||||
o->decReferenceCount();
|
||||
|
||||
data.elements [indexToChange] = newObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + 1);
|
||||
bassert (data.elements != nullptr);
|
||||
data.elements [numUsed++] = newObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds elements from another array to the end of this array.
|
||||
|
||||
@param arrayToAddFrom the array from which to copy the elements
|
||||
@param startIndex the first element of the other array to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other array. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
void addArray (const SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse>& arrayToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (arrayToAddFrom.getLock());
|
||||
|
||||
{
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (startIndex < 0)
|
||||
{
|
||||
bassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > arrayToAddFrom.size())
|
||||
numElementsToAdd = arrayToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
{
|
||||
data.ensureAllocatedSize (numUsed + numElementsToAdd);
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (arrayToAddFrom.getUnchecked (startIndex++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Inserts a new object into the array assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to find the position at which the new object
|
||||
should go. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator object to use to compare the elements - see the
|
||||
sort() method for details about this object's form
|
||||
@param newObject the new object to insert to the array
|
||||
@returns the index at which the new object was added
|
||||
@see add, sort
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int addSorted (ElementComparator& comparator, ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
insert (index, newObject);
|
||||
return index;
|
||||
}
|
||||
|
||||
/** Inserts or replaces an object in the array, assuming it is sorted.
|
||||
|
||||
This is similar to addSorted, but if a matching element already exists, then it will be
|
||||
replaced by the new one, rather than the new one being added as well.
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void addOrReplaceSorted (ElementComparator& comparator,
|
||||
ObjectClass* newObject) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
const int index = findInsertIndexInSortedArray (comparator, data.elements.getData(), newObject, 0, numUsed);
|
||||
|
||||
if (index > 0 && comparator.compareElements (newObject, data.elements [index - 1]) == 0)
|
||||
set (index - 1, newObject); // replace an existing object that matches
|
||||
else
|
||||
insert (index, newObject); // no match, so insert the new one
|
||||
}
|
||||
|
||||
/** Finds the index of an object in the array, assuming that the array is sorted.
|
||||
|
||||
This will use a comparator to do a binary-chop to find the index of the given
|
||||
element, if it exists. If the array isn't sorted, the behaviour of this
|
||||
method will be unpredictable.
|
||||
|
||||
@param comparator the comparator to use to compare the elements - see the sort()
|
||||
method for details about the form this object should take
|
||||
@param objectToLookFor the object to search for
|
||||
@returns the index of the element, or -1 if it's not found
|
||||
@see addSorted, sort
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
int indexOfSorted (ElementComparator& comparator,
|
||||
const ObjectClass* const objectToLookFor) const noexcept
|
||||
{
|
||||
(void) comparator;
|
||||
const ScopedLockType lock (getLock());
|
||||
int s = 0, e = numUsed;
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [s]) == 0)
|
||||
return s;
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
if (halfway == s)
|
||||
break;
|
||||
|
||||
if (comparator.compareElements (objectToLookFor, data.elements [halfway]) >= 0)
|
||||
s = halfway;
|
||||
else
|
||||
e = halfway;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an object from the array.
|
||||
|
||||
This will remove the object at a given index and move back all the
|
||||
subsequent objects to close the gap.
|
||||
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
The object that is removed will have its reference count decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see removeObject, removeRange
|
||||
*/
|
||||
void remove (const int indexToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (ObjectClass* o = *e)
|
||||
o->decReferenceCount();
|
||||
|
||||
--numUsed;
|
||||
const int numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes and returns an object from the array.
|
||||
|
||||
This will remove the object at a given index and return it, moving back all
|
||||
the subsequent objects to close the gap. If the index passed in is out-of-range,
|
||||
nothing will happen and a null pointer will be returned.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
ObjectClassPtr removeAndReturn (const int indexToRemove)
|
||||
{
|
||||
ObjectClassPtr removedItem;
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (indexToRemove, numUsed))
|
||||
{
|
||||
ObjectClass** const e = data.elements + indexToRemove;
|
||||
|
||||
if (ObjectClass* o = *e)
|
||||
{
|
||||
removedItem = o;
|
||||
o->decReferenceCount();
|
||||
}
|
||||
|
||||
--numUsed;
|
||||
const int numberToShift = numUsed - indexToRemove;
|
||||
|
||||
if (numberToShift > 0)
|
||||
memmove (e, e + 1, sizeof (ObjectClass*) * (size_t) numberToShift);
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
return removedItem;
|
||||
}
|
||||
|
||||
/** Removes the first occurrence of a specified object from the array.
|
||||
|
||||
If the item isn't found, no action is taken. If it is found, it is
|
||||
removed and has its reference count decreased.
|
||||
|
||||
@param objectToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeObject (ObjectClass* const objectToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
remove (indexOf (objectToRemove));
|
||||
}
|
||||
|
||||
/** Removes a range of objects from the array.
|
||||
|
||||
This will remove a set of objects, starting from the given index,
|
||||
and move any subsequent elements down to close the gap.
|
||||
|
||||
If the range extends beyond the bounds of the array, it will
|
||||
be safely clipped to the size of the array.
|
||||
|
||||
The objects that are removed will have their reference counts decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param startIndex the index of the first object to remove
|
||||
@param numberToRemove how many objects should be removed
|
||||
@see remove, removeObject
|
||||
*/
|
||||
void removeRange (const int startIndex,
|
||||
const int numberToRemove)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
const int start = blimit (0, numUsed, startIndex);
|
||||
const int endIndex = blimit (0, numUsed, startIndex + numberToRemove);
|
||||
|
||||
if (endIndex > start)
|
||||
{
|
||||
int i;
|
||||
for (i = start; i < endIndex; ++i)
|
||||
{
|
||||
if (ObjectClass* o = data.elements[i])
|
||||
{
|
||||
o->decReferenceCount();
|
||||
data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
}
|
||||
|
||||
const int rangeSize = endIndex - start;
|
||||
ObjectClass** e = data.elements + start;
|
||||
i = numUsed - endIndex;
|
||||
numUsed -= rangeSize;
|
||||
|
||||
while (--i >= 0)
|
||||
{
|
||||
*e = e [rangeSize];
|
||||
++e;
|
||||
}
|
||||
|
||||
if ((numUsed << 1) < data.numAllocated)
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the last n objects from the array.
|
||||
|
||||
The objects that are removed will have their reference counts decreased,
|
||||
and may be deleted if not referenced from elsewhere.
|
||||
|
||||
@param howManyToRemove how many objects to remove from the end of the array
|
||||
@see remove, removeObject, removeRange
|
||||
*/
|
||||
void removeLast (int howManyToRemove = 1)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (howManyToRemove > numUsed)
|
||||
howManyToRemove = numUsed;
|
||||
|
||||
while (--howManyToRemove >= 0)
|
||||
remove (numUsed - 1);
|
||||
}
|
||||
|
||||
/** Swaps a pair of objects in the array.
|
||||
|
||||
If either of the indexes passed in is out-of-range, nothing will happen,
|
||||
otherwise the two objects at these positions will be exchanged.
|
||||
*/
|
||||
void swap (const int index1,
|
||||
const int index2) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (index1, numUsed)
|
||||
&& isPositiveAndBelow (index2, numUsed))
|
||||
{
|
||||
std::swap (data.elements [index1],
|
||||
data.elements [index2]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Moves one of the objects to a different position.
|
||||
|
||||
This will move the object to a specified index, shuffling along
|
||||
any intervening elements as required.
|
||||
|
||||
So for example, if you have the array { 0, 1, 2, 3, 4, 5 } then calling
|
||||
move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
|
||||
|
||||
@param currentIndex the index of the object to be moved. If this isn't a
|
||||
valid index, then nothing will be done
|
||||
@param newIndex the index at which you'd like this object to end up. If this
|
||||
is less than zero, it will be moved to the end of the array
|
||||
*/
|
||||
void move (const int currentIndex,
|
||||
int newIndex) noexcept
|
||||
{
|
||||
if (currentIndex != newIndex)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
if (isPositiveAndBelow (currentIndex, numUsed))
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, numUsed))
|
||||
newIndex = numUsed - 1;
|
||||
|
||||
ObjectClass* const value = data.elements [currentIndex];
|
||||
|
||||
if (newIndex > currentIndex)
|
||||
{
|
||||
memmove (data.elements + currentIndex,
|
||||
data.elements + currentIndex + 1,
|
||||
sizeof (ObjectClass*) * (size_t) (newIndex - currentIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove (data.elements + newIndex + 1,
|
||||
data.elements + newIndex,
|
||||
sizeof (ObjectClass*) * (size_t) (currentIndex - newIndex));
|
||||
}
|
||||
|
||||
data.elements [newIndex] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** This swaps the contents of this array with those of another array.
|
||||
|
||||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
|
||||
because it just swaps their internal pointers.
|
||||
*/
|
||||
template <class OtherArrayType>
|
||||
void swapWith (OtherArrayType& otherArray) noexcept
|
||||
{
|
||||
const ScopedLockType lock1 (getLock());
|
||||
const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock());
|
||||
|
||||
data.swapWith (otherArray.data);
|
||||
std::swap (numUsed, otherArray.numUsed);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this array to another one.
|
||||
|
||||
@returns true only if the other array contains the same objects in the same order
|
||||
*/
|
||||
bool operator== (const SharedObjectArray& other) const noexcept
|
||||
{
|
||||
const ScopedLockType lock2 (other.getLock());
|
||||
const ScopedLockType lock1 (getLock());
|
||||
|
||||
if (numUsed != other.numUsed)
|
||||
return false;
|
||||
|
||||
for (int i = numUsed; --i >= 0;)
|
||||
if (data.elements [i] != other.data.elements [i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Compares this array to another one.
|
||||
|
||||
@see operator==
|
||||
*/
|
||||
bool operator!= (const SharedObjectArray<ObjectClass, TypeOfCriticalSectionToUse>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Sorts the elements in the array.
|
||||
|
||||
This will use a comparator object to sort the elements into order. The object
|
||||
passed must have a method of the form:
|
||||
@code
|
||||
int compareElements (ElementType first, ElementType second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator the comparator to use for comparing elements.
|
||||
@param retainOrderOfEquivalentItems if this is true, then items
|
||||
which the comparator says are equivalent will be
|
||||
kept in the order in which they currently appear
|
||||
in the array. This is slower to perform, but may
|
||||
be important in some cases. If it's false, a faster
|
||||
algorithm is used, but equivalent elements may be
|
||||
rearranged.
|
||||
|
||||
@see sortArray
|
||||
*/
|
||||
template <class ElementComparator>
|
||||
void sort (ElementComparator& comparator,
|
||||
const bool retainOrderOfEquivalentItems = false) const noexcept
|
||||
{
|
||||
(void) comparator; // if you pass in an object with a static compareElements() method, this
|
||||
// avoids getting warning messages about the parameter being unused
|
||||
|
||||
const ScopedLockType lock (getLock());
|
||||
sortArray (comparator, data.elements.getData(), 0, size() - 1, retainOrderOfEquivalentItems);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the array.
|
||||
|
||||
Arrays typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.shrinkToNoMoreThan (numUsed);
|
||||
}
|
||||
|
||||
/** Increases the array's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the array won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.ensureAllocatedSize (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ArrayAllocationBase <ObjectClass*, TypeOfCriticalSectionToUse> data;
|
||||
int numUsed;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,212 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SHAREDTABLE_BEASTHEADER
|
||||
#define BEAST_SHAREDTABLE_BEASTHEADER
|
||||
|
||||
/** Handle to a reference counted fixed size table.
|
||||
|
||||
@note Currently, ElementType must be an aggregate of POD.
|
||||
|
||||
@tparam ElementType The type of element.
|
||||
|
||||
@ingroup beast_basics
|
||||
*/
|
||||
template <class ElementType>
|
||||
class SharedTable
|
||||
{
|
||||
public:
|
||||
typedef ElementType Entry;
|
||||
|
||||
static SharedTable <ElementType> const null;
|
||||
|
||||
/** Creates a null table.
|
||||
*/
|
||||
SharedTable ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a table with the specified number of entries.
|
||||
|
||||
The entries are uninitialized.
|
||||
|
||||
@param numEntries The number of entries in the table.
|
||||
|
||||
@todo Initialize the data if ElementType is not POD.
|
||||
*/
|
||||
explicit SharedTable (int numEntries)
|
||||
: m_data (new Data (numEntries))
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a shared reference to another table.
|
||||
*/
|
||||
SharedTable (SharedTable const& other)
|
||||
: m_data (other.m_data)
|
||||
{
|
||||
}
|
||||
|
||||
/** Makes this table refer to another table.
|
||||
*/
|
||||
SharedTable& operator= (SharedTable const& other)
|
||||
{
|
||||
m_data = other.m_data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
SharedTable (SharedTable&& other) noexcept
|
||||
: m_data (static_cast < typename Data::Ptr&& > (other.m_data))
|
||||
{
|
||||
}
|
||||
|
||||
SharedTable& operator= (SharedTable && other) noexcept
|
||||
{
|
||||
m_data = static_cast < typename Data::Ptr && > (other.m_data);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Destructor.
|
||||
*/
|
||||
~SharedTable ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns true if the two tables share the same set of entries.
|
||||
*/
|
||||
bool operator== (SharedTable const& other) const noexcept
|
||||
{
|
||||
return m_data == other.m_data;
|
||||
}
|
||||
|
||||
/** Returns true if the two tables do not share the same set of entries.
|
||||
*/
|
||||
bool operator!= (SharedTable const& other) const noexcept
|
||||
{
|
||||
return m_data != other.m_data;
|
||||
}
|
||||
|
||||
/** Returns true if the table is not null.
|
||||
*/
|
||||
inline bool isValid () const noexcept
|
||||
{
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
/** Returns true if the table is null.
|
||||
*/
|
||||
inline bool isNull () const noexcept
|
||||
{
|
||||
return m_data == nullptr;
|
||||
}
|
||||
|
||||
/** Returns the number of tables referring to the same shared entries.
|
||||
*/
|
||||
int getReferenceCount () const noexcept
|
||||
{
|
||||
return m_data == nullptr ? 0 : m_data->getReferenceCount ();
|
||||
}
|
||||
|
||||
/** Create a physical duplicate of the table.
|
||||
*/
|
||||
SharedTable createCopy () const
|
||||
{
|
||||
return SharedTable (m_data != nullptr ? m_data->clone () : nullptr);
|
||||
}
|
||||
|
||||
/** Makes sure no other tables share the same entries as this table.
|
||||
*/
|
||||
void duplicateIfShared ()
|
||||
{
|
||||
if (m_data != nullptr && m_data->getReferenceCount () > 1)
|
||||
m_data = m_data->clone ();
|
||||
}
|
||||
|
||||
/** Return the number of entries in this table.
|
||||
*/
|
||||
inline int getNumEntries () const noexcept
|
||||
{
|
||||
return m_data->getNumEntries ();
|
||||
}
|
||||
|
||||
/** Retrieve a table entry.
|
||||
|
||||
@param index The index of the entry, from 0 to getNumEntries ().
|
||||
*/
|
||||
inline ElementType& operator [] (int index) const noexcept
|
||||
{
|
||||
return m_data->getReference (index);
|
||||
}
|
||||
|
||||
private:
|
||||
class Data : public SharedObject
|
||||
{
|
||||
public:
|
||||
typedef SharedObjectPtr <Data> Ptr;
|
||||
|
||||
explicit Data (int numEntries)
|
||||
: m_numEntries (numEntries)
|
||||
, m_table (numEntries)
|
||||
{
|
||||
}
|
||||
|
||||
inline Data* clone () const
|
||||
{
|
||||
Data* data = new Data (m_numEntries);
|
||||
|
||||
memcpy (
|
||||
data->m_table.getData (),
|
||||
m_table.getData (),
|
||||
m_numEntries * sizeof (ElementType));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
inline int getNumEntries () const
|
||||
{
|
||||
return m_numEntries;
|
||||
}
|
||||
|
||||
inline ElementType& getReference (int index) const
|
||||
{
|
||||
bassert (index >= 0 && index < m_numEntries);
|
||||
return m_table [index];
|
||||
}
|
||||
|
||||
private:
|
||||
int const m_numEntries;
|
||||
HeapBlock <ElementType> const m_table;
|
||||
};
|
||||
|
||||
explicit SharedTable (Data* data)
|
||||
: m_data (data)
|
||||
{
|
||||
}
|
||||
|
||||
SharedObjectPtr <Data> m_data;
|
||||
};
|
||||
|
||||
template <class ElementType>
|
||||
SharedTable <ElementType> const SharedTable <ElementType>::null;
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SORTEDLOOKUPTABLE_BEASTHEADER
|
||||
#define BEAST_SORTEDLOOKUPTABLE_BEASTHEADER
|
||||
|
||||
/** Sorted map for fast lookups.
|
||||
|
||||
This container is optimized for a data set with fixed elements.
|
||||
|
||||
SchemaType obeys this concept:
|
||||
|
||||
@code
|
||||
|
||||
struct SchemaType
|
||||
{
|
||||
typename KeyType;
|
||||
typename ValueType;
|
||||
|
||||
// Retrieve the key for a specified value.
|
||||
KeyType getKey (Value const& value);
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
To use the table, reserve space with reserveSpaceForValues() if the number
|
||||
of elements is known ahead of time. Then, call insert() for all the your
|
||||
elements. Call prepareForLookups() once then call lookupValueByKey ()
|
||||
*/
|
||||
template <class SchemaType>
|
||||
class SortedLookupTable
|
||||
{
|
||||
private:
|
||||
typedef typename SchemaType::KeyType KeyType;
|
||||
typedef typename SchemaType::ValueType ValueType;
|
||||
typedef std::vector <ValueType> values_t;
|
||||
|
||||
private:
|
||||
struct SortCompare
|
||||
{
|
||||
bool operator () (ValueType const& lhs, ValueType const& rhs) const
|
||||
{
|
||||
return SchemaType ().getKey (lhs) < SchemaType ().getKey (rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct FindCompare
|
||||
{
|
||||
bool operator () (ValueType const& lhs, ValueType const& rhs)
|
||||
{
|
||||
return SchemaType ().getKey (lhs) < SchemaType ().getKey (rhs);
|
||||
}
|
||||
|
||||
bool operator () (KeyType const& key, ValueType const& rhs)
|
||||
{
|
||||
return key < SchemaType ().getKey (rhs);
|
||||
}
|
||||
|
||||
bool operator () (ValueType const& lhs, KeyType const& key)
|
||||
{
|
||||
return SchemaType ().getKey (lhs) < key;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
typedef typename values_t::size_type size_type;
|
||||
|
||||
/** Reserve space for values.
|
||||
|
||||
Although not necessary, this can help with memory usage if the
|
||||
number of values is known ahead of time.
|
||||
|
||||
@param numberOfValues The amount of space to reserve.
|
||||
*/
|
||||
void reserveSpaceForValues (size_type numberOfValues)
|
||||
{
|
||||
m_values.reserve (numberOfValues);
|
||||
}
|
||||
|
||||
/** Insert a value into the index.
|
||||
|
||||
@invariant The value must not already exist in the index.
|
||||
|
||||
@param valueToInsert The value to insert.
|
||||
*/
|
||||
void insert (ValueType const& valueToInsert)
|
||||
{
|
||||
m_values.push_back (valueToInsert);
|
||||
}
|
||||
|
||||
/** Prepare the index for lookups.
|
||||
|
||||
This must be called at least once after calling insert()
|
||||
and before calling find().
|
||||
*/
|
||||
void prepareForLookups ()
|
||||
{
|
||||
std::sort (m_values.begin (), m_values.end (), SortCompare ());
|
||||
}
|
||||
|
||||
/** Find the value for a key.
|
||||
|
||||
Quickly locates a value matching the key, or returns false
|
||||
indicating no value was found.
|
||||
|
||||
@invariant You must call prepareForLookups() once, after all
|
||||
insertions, before calling this function.
|
||||
|
||||
@param key The key to locate.
|
||||
@param pFoundValue Pointer to store the value if a matching
|
||||
key was found.
|
||||
@return `true` if the value was found.
|
||||
*/
|
||||
bool lookupValueByKey (KeyType const& key, ValueType* pFoundValue)
|
||||
{
|
||||
bool found;
|
||||
|
||||
std::pair <typename values_t::iterator, typename values_t::iterator> result =
|
||||
std::equal_range (m_values.begin (), m_values.end (), key, FindCompare ());
|
||||
|
||||
if (result.first != result.second)
|
||||
{
|
||||
*pFoundValue = *result.first;
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
found = false;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private:
|
||||
values_t m_values;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,495 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_SORTEDSET_BEASTHEADER
|
||||
#define BEAST_SORTEDSET_BEASTHEADER
|
||||
|
||||
#include "beast_ArrayAllocationBase.h"
|
||||
#include "../threads/beast_CriticalSection.h"
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4512)
|
||||
#endif
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds a set of unique primitive objects, such as ints or doubles.
|
||||
|
||||
A set can only hold one item with a given value, so if for example it's a
|
||||
set of integers, attempting to add the same integer twice will do nothing
|
||||
the second time.
|
||||
|
||||
Internally, the list of items is kept sorted (which means that whatever
|
||||
kind of primitive type is used must support the ==, <, >, <= and >= operators
|
||||
to determine the order), and searching the set for known values is very fast
|
||||
because it uses a binary-chop method.
|
||||
|
||||
Note that if you're using a class or struct as the element type, it must be
|
||||
capable of being copied or moved with a straightforward memcpy, rather than
|
||||
needing construction and destruction code.
|
||||
|
||||
To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
|
||||
TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
|
||||
|
||||
@see Array, OwnedArray, SharedObjectArray, StringArray, CriticalSection
|
||||
*/
|
||||
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
|
||||
class SortedSet
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
SortedSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another set.
|
||||
@param other the set to copy
|
||||
*/
|
||||
SortedSet (const SortedSet& other)
|
||||
: data (other.data)
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~SortedSet() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
/** Copies another set over this one.
|
||||
@param other the set to copy
|
||||
*/
|
||||
SortedSet& operator= (const SortedSet& other) noexcept
|
||||
{
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator== (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return data == other.data;
|
||||
}
|
||||
|
||||
/** Compares this set to another one.
|
||||
Two sets are considered equal if they both contain the same set of elements.
|
||||
@param other the other set to compare with
|
||||
*/
|
||||
bool operator!= (const SortedSet<ElementType>& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all elements from the set.
|
||||
|
||||
This will remove all the elements, and free any storage that the set is
|
||||
using. To clear it without freeing the storage, use the clearQuick()
|
||||
method instead.
|
||||
|
||||
@see clearQuick
|
||||
*/
|
||||
void clear() noexcept
|
||||
{
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/** Removes all elements from the set without freeing the array's allocated storage.
|
||||
|
||||
@see clear
|
||||
*/
|
||||
void clearQuick() noexcept
|
||||
{
|
||||
data.clearQuick();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current number of elements in the set.
|
||||
*/
|
||||
inline int size() const noexcept
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set.
|
||||
|
||||
If the index passed in is beyond the range of valid elements, this
|
||||
will return zero.
|
||||
|
||||
If you're certain that the index will always be a valid element, you
|
||||
can call getUnchecked() instead, which is faster.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see getUnchecked, getFirst, getLast
|
||||
*/
|
||||
inline ElementType operator[] (const int index) const noexcept
|
||||
{
|
||||
return data [index];
|
||||
}
|
||||
|
||||
/** Returns one of the elements in the set, without checking the index passed in.
|
||||
Unlike the operator[] method, this will try to return an element without
|
||||
checking that the index is within the bounds of the set, so should only
|
||||
be used when you're confident that it will always be a valid index.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the set)
|
||||
@see operator[], getFirst, getLast
|
||||
*/
|
||||
inline ElementType getUnchecked (const int index) const noexcept
|
||||
{
|
||||
return data.getUnchecked (index);
|
||||
}
|
||||
|
||||
/** Returns a direct reference to one of the elements in the set, without checking the index passed in.
|
||||
|
||||
This is like getUnchecked, but returns a direct reference to the element, so that
|
||||
you can alter it directly. Obviously this can be dangerous, so only use it when
|
||||
absolutely necessary.
|
||||
|
||||
@param index the index of the element being requested (0 is the first element in the array)
|
||||
*/
|
||||
inline ElementType& getReference (const int index) const noexcept
|
||||
{
|
||||
return data.getReference (index);
|
||||
}
|
||||
|
||||
/** Returns the first element in the set, or 0 if the set is empty.
|
||||
|
||||
@see operator[], getUnchecked, getLast
|
||||
*/
|
||||
inline ElementType getFirst() const noexcept
|
||||
{
|
||||
return data.getFirst();
|
||||
}
|
||||
|
||||
/** Returns the last element in the set, or 0 if the set is empty.
|
||||
|
||||
@see operator[], getUnchecked, getFirst
|
||||
*/
|
||||
inline ElementType getLast() const noexcept
|
||||
{
|
||||
return data.getLast();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a pointer to the first element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ElementType* begin() const noexcept
|
||||
{
|
||||
return data.begin();
|
||||
}
|
||||
|
||||
/** Returns a pointer to the element which follows the last element in the set.
|
||||
This method is provided for compatibility with standard C++ iteration mechanisms.
|
||||
*/
|
||||
inline ElementType* end() const noexcept
|
||||
{
|
||||
return data.end();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the index of the first element which matches the value passed in.
|
||||
|
||||
This will search the set for the given object, and return the index
|
||||
of its first occurrence. If the object isn't found, the method will return -1.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns the index of the object, or -1 if it's not found
|
||||
*/
|
||||
int indexOf (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
const ScopedLockType lock (data.getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (s >= e)
|
||||
return -1;
|
||||
|
||||
if (elementToLookFor == data.getReference (s))
|
||||
return s;
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
|
||||
if (halfway == s)
|
||||
return -1;
|
||||
else if (elementToLookFor < data.getReference (halfway))
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the set contains at least one occurrence of an object.
|
||||
|
||||
@param elementToLookFor the value or object to look for
|
||||
@returns true if the item is found
|
||||
*/
|
||||
bool contains (const ElementType& elementToLookFor) const noexcept
|
||||
{
|
||||
return indexOf (elementToLookFor) >= 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a new element to the set, (as long as it's not already in there).
|
||||
|
||||
Note that if a matching element already exists, the new value will be assigned
|
||||
to the existing one using operator=, so that if there are any differences between
|
||||
the objects which were not recognised by the object's operator==, then the
|
||||
set will always contain a copy of the most recently added one.
|
||||
|
||||
@param newElement the new object to add to the set
|
||||
@returns true if the value was added, or false if it already existed
|
||||
@see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
|
||||
*/
|
||||
bool add (const ElementType& newElement) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
int s = 0;
|
||||
int e = data.size();
|
||||
|
||||
while (s < e)
|
||||
{
|
||||
ElementType& elem = data.getReference (s);
|
||||
if (newElement == elem)
|
||||
{
|
||||
elem = newElement; // force an update in case operator== permits differences.
|
||||
return false;
|
||||
}
|
||||
|
||||
const int halfway = (s + e) / 2;
|
||||
const bool isBeforeHalfway = (newElement < data.getReference (halfway));
|
||||
|
||||
if (halfway == s)
|
||||
{
|
||||
if (! isBeforeHalfway)
|
||||
++s;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (isBeforeHalfway)
|
||||
e = halfway;
|
||||
else
|
||||
s = halfway;
|
||||
}
|
||||
|
||||
data.insert (s, newElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Adds elements from an array to this set.
|
||||
|
||||
@param elementsToAdd the array of elements to add
|
||||
@param numElementsToAdd how many elements are in this other array
|
||||
@see add
|
||||
*/
|
||||
void addArray (const ElementType* elementsToAdd,
|
||||
int numElementsToAdd) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
|
||||
while (--numElementsToAdd >= 0)
|
||||
add (*elementsToAdd++);
|
||||
}
|
||||
|
||||
/** Adds elements from another set to this one.
|
||||
|
||||
@param setToAddFrom the set from which to copy the elements
|
||||
@param startIndex the first element of the other set to start copying from
|
||||
@param numElementsToAdd how many elements to add from the other set. If this
|
||||
value is negative or greater than the number of available elements,
|
||||
all available elements will be copied.
|
||||
@see add
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void addSet (const OtherSetType& setToAddFrom,
|
||||
int startIndex = 0,
|
||||
int numElementsToAdd = -1) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
|
||||
|
||||
{
|
||||
const ScopedLockType lock2 (getLock());
|
||||
bassert (this != &setToAddFrom);
|
||||
|
||||
if (this != &setToAddFrom)
|
||||
{
|
||||
if (startIndex < 0)
|
||||
{
|
||||
bassertfalse;
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
|
||||
numElementsToAdd = setToAddFrom.size() - startIndex;
|
||||
|
||||
if (numElementsToAdd > 0)
|
||||
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Removes an element from the set.
|
||||
|
||||
This will remove the element at a given index.
|
||||
If the index passed in is out-of-range, nothing will happen.
|
||||
|
||||
@param indexToRemove the index of the element to remove
|
||||
@returns the element that has been removed
|
||||
@see removeValue, removeRange
|
||||
*/
|
||||
ElementType remove (const int indexToRemove) noexcept
|
||||
{
|
||||
return data.remove (indexToRemove);
|
||||
}
|
||||
|
||||
/** Removes an item from the set.
|
||||
|
||||
This will remove the given element from the set, if it's there.
|
||||
|
||||
@param valueToRemove the object to try to remove
|
||||
@see remove, removeRange
|
||||
*/
|
||||
void removeValue (const ElementType valueToRemove) noexcept
|
||||
{
|
||||
const ScopedLockType lock (getLock());
|
||||
data.remove (indexOf (valueToRemove));
|
||||
}
|
||||
|
||||
/** Removes any elements which are also in another set.
|
||||
|
||||
@param otherSet the other set in which to look for elements to remove
|
||||
@see removeValuesNotIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this == &otherSet)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else if (otherSet.size() > 0)
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes any elements which are not found in another set.
|
||||
|
||||
Only elements which occur in this other set will be retained.
|
||||
|
||||
@param otherSet the set in which to look for elements NOT to remove
|
||||
@see removeValuesIn, remove, removeValue, removeRange
|
||||
*/
|
||||
template <class OtherSetType>
|
||||
void removeValuesNotIn (const OtherSetType& otherSet) noexcept
|
||||
{
|
||||
const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
|
||||
const ScopedLockType lock2 (getLock());
|
||||
|
||||
if (this != &otherSet)
|
||||
{
|
||||
if (otherSet.size() <= 0)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = data.size(); --i >= 0;)
|
||||
if (! otherSet.contains (data.getReference (i)))
|
||||
remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This swaps the contents of this array with those of another array.
|
||||
|
||||
If you need to exchange two arrays, this is vastly quicker than using copy-by-value
|
||||
because it just swaps their internal pointers.
|
||||
*/
|
||||
template <class OtherSortedSetType>
|
||||
void swapWith (OtherSortedSetType& otherSet) noexcept
|
||||
{
|
||||
data.swapWith (otherSet.data);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Reduces the amount of storage being used by the set.
|
||||
|
||||
Sets typically allocate slightly more storage than they need, and after
|
||||
removing elements, they may have quite a lot of unused space allocated.
|
||||
This method will reduce the amount of allocated storage to a minimum.
|
||||
*/
|
||||
void minimiseStorageOverheads() noexcept
|
||||
{
|
||||
data.minimiseStorageOverheads();
|
||||
}
|
||||
|
||||
/** Increases the set's internal storage to hold a minimum number of elements.
|
||||
|
||||
Calling this before adding a large known number of elements means that
|
||||
the set won't have to keep dynamically resizing itself as the elements
|
||||
are added, and it'll therefore be more efficient.
|
||||
*/
|
||||
void ensureStorageAllocated (const int minNumElements)
|
||||
{
|
||||
data.ensureStorageAllocated (minNumElements);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the CriticalSection that locks this array.
|
||||
To lock, you can call getLock().enter() and getLock().exit(), or preferably use
|
||||
an object of ScopedLockType as an RAII lock for it.
|
||||
*/
|
||||
inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); }
|
||||
|
||||
/** Returns the type of scoped lock to use for locking this array */
|
||||
typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <ElementType, TypeOfCriticalSectionToUse> data;
|
||||
};
|
||||
|
||||
#if BEAST_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // BEAST_SORTEDSET_BEASTHEADER
|
||||
@@ -1,334 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#if 0
|
||||
#include <iostream>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Windows structured exception handling
|
||||
//
|
||||
#if BEAST_MSVC
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace vf
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
//
|
||||
// While this object is in scope, any Windows SEH
|
||||
// exceptions will be caught and re-thrown as an Error object.
|
||||
//
|
||||
class ScopedPlatformExceptionCatcher : public Uncopyable
|
||||
{
|
||||
public:
|
||||
ScopedPlatformExceptionCatcher ()
|
||||
{
|
||||
//s_mutex.enter ();
|
||||
|
||||
if (++s_count == 1)
|
||||
s_sehPrev = ::SetUnhandledExceptionFilter (sehFilter);
|
||||
|
||||
//s_mutex.exit ();
|
||||
}
|
||||
|
||||
~ScopedPlatformExceptionCatcher ()
|
||||
{
|
||||
//s_mutex.enter ();
|
||||
|
||||
if (--s_count == 0)
|
||||
SetUnhandledExceptionFilter (s_sehPrev);
|
||||
|
||||
//s_mutex.exit ();
|
||||
}
|
||||
|
||||
static LONG WINAPI sehFilter (_EXCEPTION_POINTERS* ei)
|
||||
{
|
||||
EXCEPTION_RECORD* er = ei->ExceptionRecord;
|
||||
|
||||
if (er->ExceptionCode == EXCEPTION_BREAKPOINT ||
|
||||
er->ExceptionCode == EXCEPTION_SINGLE_STEP)
|
||||
{
|
||||
// pass through
|
||||
}
|
||||
else
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (er->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
s = TRANS ("an access violation occurred");
|
||||
break;
|
||||
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
s = TRANS ("array bounds were exceeded");
|
||||
break;
|
||||
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
s = TRANS ("memory access was unaligned");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
s = TRANS ("a floating point operation produced a denormal");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
s = TRANS ("a floating point divide by zero was attempted");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
s = TRANS ("the floating point operation was unrepresentable");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
s = TRANS ("the floating point operation was invalid");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
s = TRANS ("the floating point operation overflowed");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
s = TRANS ("a stack check resulted from a floating point operation");
|
||||
break;
|
||||
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
s = TRANS ("the floating point operation underflowed");
|
||||
break;
|
||||
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
s = TRANS ("an invalid instruction was received");
|
||||
break;
|
||||
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
s = TRANS ("a virtual paging error occurred");
|
||||
break;
|
||||
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
s = TRANS ("an integer divide by zero was attempted");
|
||||
break;
|
||||
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
s = TRANS ("an integer operation overflowed");
|
||||
break;
|
||||
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
s = TRANS ("the exception handler returned an invalid disposition");
|
||||
break;
|
||||
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
s = TRANS ("a non-continuable exception occurred");
|
||||
break;
|
||||
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
s = TRANS ("a privileged instruction was attempted");
|
||||
break;
|
||||
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
s = TRANS ("the stack overflowed");
|
||||
break;
|
||||
|
||||
default:
|
||||
s = TRANS ("an unknown system exception of code ");
|
||||
s << String ((unsigned int)er->ExceptionCode);
|
||||
s << " " << TRANS ("occurred");
|
||||
break;
|
||||
}
|
||||
|
||||
Throw (Error ().fail (__FILE__, __LINE__, s, Error::platform));
|
||||
}
|
||||
|
||||
return s_sehPrev (ei);
|
||||
}
|
||||
|
||||
private:
|
||||
static int s_count;
|
||||
static CriticalSection s_mutex;
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER s_sehPrev;
|
||||
};
|
||||
|
||||
CriticalSection ScopedPlatformExceptionCatcher::s_mutex;
|
||||
int ScopedPlatformExceptionCatcher::s_count = 0;
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER ScopedPlatformExceptionCatcher::s_sehPrev = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#else
|
||||
|
||||
// TODO: POSIX SIGNAL HANDLER
|
||||
|
||||
#pragma message(BEAST_FILEANDLINE_ "Missing class ScopedPlatformExceptionCatcher")
|
||||
|
||||
namespace vf
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class ScopedPlatformExceptionCatcher
|
||||
{
|
||||
public:
|
||||
// Missing
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
END_BEAST_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if 0
|
||||
bool CatchAny (Function <void (void)> f, bool returnFromException)
|
||||
{
|
||||
bool caughtException = true; // assume the worst
|
||||
|
||||
try
|
||||
{
|
||||
//ScopedPlatformExceptionCatcher platformExceptionCatcher;
|
||||
|
||||
f ();
|
||||
|
||||
caughtException = false;
|
||||
}
|
||||
catch (Error& e)
|
||||
{
|
||||
if (!returnFromException)
|
||||
{
|
||||
JUCEApplication* app = JUCEApplication::getInstance ();
|
||||
|
||||
if (app)
|
||||
{
|
||||
app->unhandledException (
|
||||
&e,
|
||||
e.getSourceFilename (),
|
||||
e.getLineNumber ());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << e.what ();
|
||||
std::unexpected ();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
if (!returnFromException)
|
||||
{
|
||||
JUCEApplication* app = JUCEApplication::getInstance ();
|
||||
|
||||
if (app)
|
||||
{
|
||||
app->unhandledException (&e, __FILE__, __LINE__);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << e.what ();
|
||||
std::unexpected ();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (!returnFromException)
|
||||
{
|
||||
JUCEApplication* app = JUCEApplication::getInstance ();
|
||||
|
||||
if (app)
|
||||
{
|
||||
app->unhandledException (0, __FILE__, __LINE__);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unexpected ();
|
||||
}
|
||||
}
|
||||
}
|
||||
return caughtException;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void ProtectedCall::DefaultHandler::onException (ProtectedCall::Exception const&) const
|
||||
{
|
||||
}
|
||||
|
||||
Static::Storage <Atomic <ProtectedCall::Handler const*>, ProtectedCall>
|
||||
ProtectedCall::s_handler;
|
||||
|
||||
void ProtectedCall::setHandler (Handler const& handler)
|
||||
{
|
||||
s_handler->set (&handler);
|
||||
}
|
||||
|
||||
void ProtectedCall::call (Call& c)
|
||||
{
|
||||
static DefaultHandler defaultHandler;
|
||||
|
||||
Handler const* handler = s_handler->get ();
|
||||
|
||||
if (handler == nullptr)
|
||||
handler = &defaultHandler;
|
||||
|
||||
try
|
||||
{
|
||||
c ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Exception e;
|
||||
|
||||
handler->onException (e);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ProtectedCallTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ProtectedCallTests () : UnitTest ("ProtectedCall", "beast", runManual)
|
||||
{
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
beginTestCase ("backtrace");
|
||||
|
||||
String const s = SystemStats::getStackBacktrace ();
|
||||
|
||||
logMessage (s);
|
||||
|
||||
pass ();
|
||||
}
|
||||
};
|
||||
|
||||
static ProtectedCallTests protectedCallTests;
|
||||
@@ -1,163 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_PROTECTEDCALL_H_INCLUDED
|
||||
#define BEAST_PROTECTEDCALL_H_INCLUDED
|
||||
|
||||
/** Call a function in a protected exception context.
|
||||
|
||||
This is for intercepting unhandled exceptions, reporting on the extended
|
||||
information provided by @ref Throw, and calling a customizable unhandled
|
||||
exception callback. Some implementations will also catch native exceptions
|
||||
such as memory violations or segmentation faults.
|
||||
|
||||
An unhandled exception should terminate the process with a non zero
|
||||
return code.
|
||||
|
||||
To use this, construct an instance with your funtion and arguments as
|
||||
parameters. For example:
|
||||
|
||||
@code
|
||||
|
||||
extern void funcThatMightThrow (int numberOfTimes);
|
||||
|
||||
ProtectedCall (&funcThatMightThrow, 3);
|
||||
|
||||
@endcode
|
||||
*/
|
||||
class ProtectedCall
|
||||
{
|
||||
public:
|
||||
struct Exception
|
||||
{
|
||||
};
|
||||
|
||||
/** This receives the unhandled exception. */
|
||||
struct Handler
|
||||
{
|
||||
/** Called when an uhandled exception is thrown.
|
||||
|
||||
@note This can be called from multiple threads, which is
|
||||
why it is const.
|
||||
*/
|
||||
virtual void onException (Exception const& e) const = 0;
|
||||
};
|
||||
|
||||
/** The default handler writes to std::cerr makes the process exit. */
|
||||
class DefaultHandler : public Handler
|
||||
{
|
||||
void onException (Exception const& e) const;
|
||||
};
|
||||
|
||||
static void setHandler (Handler const& handler);
|
||||
|
||||
public:
|
||||
#if BEAST_VARIADIC_MAX >= 1
|
||||
template <class Fn>
|
||||
explicit ProtectedCall (Fn f)
|
||||
{ callf (functional::bind (f)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 2
|
||||
template <class Fn, class T1>
|
||||
ProtectedCall (Fn f, T1 t1)
|
||||
{ callf (functional::bind (f, t1)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 3
|
||||
template <class Fn, class T1, class T2>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2)
|
||||
{ callf (functional::bind (f, t1, t2)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 4
|
||||
template <class Fn, class T1, class T2, class T3>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2, T3 t3)
|
||||
{ callf (functional::bind (f, t1, t2, t3)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 5
|
||||
template <class Fn, class T1, class T2, class T3, class T4>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2, T3 t3, T4 t4)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 6
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 7
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 8
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6, t7)); }
|
||||
#endif
|
||||
|
||||
#if BEAST_VARIADIC_MAX >= 9
|
||||
template <class Fn, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
|
||||
ProtectedCall (Fn f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
|
||||
{ callf (functional::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct Call
|
||||
{
|
||||
virtual void operator() () = 0;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
struct CallType : public Call
|
||||
{
|
||||
public:
|
||||
explicit CallType (Functor f)
|
||||
: m_f (f)
|
||||
{
|
||||
}
|
||||
|
||||
void operator() ()
|
||||
{
|
||||
m_f ();
|
||||
}
|
||||
|
||||
private:
|
||||
Functor m_f;
|
||||
};
|
||||
|
||||
template <class Functor>
|
||||
void callf (Functor f)
|
||||
{
|
||||
CallType <Functor> wrapper (f);
|
||||
|
||||
call (wrapper);
|
||||
}
|
||||
|
||||
void call (Call& call);
|
||||
|
||||
private:
|
||||
static Static::Storage <Atomic <Handler const*>, ProtectedCall> s_handler;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_EXPRESSION_BEASTHEADER
|
||||
#define BEAST_EXPRESSION_BEASTHEADER
|
||||
|
||||
#include "../memory/beast_SharedObject.h"
|
||||
#include "../containers/beast_Array.h"
|
||||
#include "../memory/beast_ScopedPointer.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for dynamically evaluating simple numeric expressions.
|
||||
|
||||
This class can parse a simple C-style string expression involving floating point
|
||||
numbers, named symbols and functions. The basic arithmetic operations of +, -, *, /
|
||||
are supported, as well as parentheses, and any alphanumeric identifiers are
|
||||
assumed to be named symbols which will be resolved when the expression is
|
||||
evaluated.
|
||||
|
||||
Expressions which use identifiers and functions require a subclass of
|
||||
Expression::Scope to be supplied when evaluating them, and this object
|
||||
is expected to be able to resolve the symbol names and perform the functions that
|
||||
are used.
|
||||
*/
|
||||
class BEAST_API Expression
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a simple expression with a value of 0. */
|
||||
Expression();
|
||||
|
||||
/** Destructor. */
|
||||
~Expression();
|
||||
|
||||
/** Creates a simple expression with a specified constant value. */
|
||||
explicit Expression (double constant);
|
||||
|
||||
/** Creates a copy of an expression. */
|
||||
Expression (const Expression& other);
|
||||
|
||||
/** Copies another expression. */
|
||||
Expression& operator= (const Expression& other);
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
Expression (Expression&& other) noexcept;
|
||||
Expression& operator= (Expression&& other) noexcept;
|
||||
#endif
|
||||
|
||||
/** Creates an expression by parsing a string.
|
||||
If there's a syntax error in the string, this will throw a ParseError exception.
|
||||
@throws ParseError
|
||||
*/
|
||||
explicit Expression (const String& stringToParse);
|
||||
|
||||
/** Returns a string version of the expression. */
|
||||
String toString() const;
|
||||
|
||||
/** Returns an expression which is an addtion operation of two existing expressions. */
|
||||
Expression operator+ (const Expression& other) const;
|
||||
/** Returns an expression which is a subtraction operation of two existing expressions. */
|
||||
Expression operator- (const Expression& other) const;
|
||||
/** Returns an expression which is a multiplication operation of two existing expressions. */
|
||||
Expression operator* (const Expression& other) const;
|
||||
/** Returns an expression which is a division operation of two existing expressions. */
|
||||
Expression operator/ (const Expression& other) const;
|
||||
/** Returns an expression which performs a negation operation on an existing expression. */
|
||||
Expression operator-() const;
|
||||
|
||||
/** Returns an Expression which is an identifier reference. */
|
||||
static Expression symbol (const String& symbol);
|
||||
|
||||
/** Returns an Expression which is a function call. */
|
||||
static Expression function (const String& functionName, const Array<Expression>& parameters);
|
||||
|
||||
/** Returns an Expression which parses a string from a character pointer, and updates the pointer
|
||||
to indicate where it finished.
|
||||
|
||||
The pointer is incremented so that on return, it indicates the character that follows
|
||||
the end of the expression that was parsed.
|
||||
|
||||
If there's a syntax error in the string, this will throw a ParseError exception.
|
||||
@throws ParseError
|
||||
*/
|
||||
static Expression parse (String::CharPointerType& stringToParse);
|
||||
|
||||
//==============================================================================
|
||||
/** When evaluating an Expression object, this class is used to resolve symbols and
|
||||
perform functions that the expression uses.
|
||||
*/
|
||||
class BEAST_API Scope
|
||||
{
|
||||
public:
|
||||
Scope();
|
||||
virtual ~Scope();
|
||||
|
||||
/** Returns some kind of globally unique ID that identifies this scope. */
|
||||
virtual String getScopeUID() const;
|
||||
|
||||
/** Returns the value of a symbol.
|
||||
If the symbol is unknown, this can throw an Expression::EvaluationError exception.
|
||||
The member value is set to the part of the symbol that followed the dot, if there is
|
||||
one, e.g. for "foo.bar", symbol = "foo" and member = "bar".
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
virtual Expression getSymbolValue (const String& symbol) const;
|
||||
|
||||
/** Executes a named function.
|
||||
If the function name is unknown, this can throw an Expression::EvaluationError exception.
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
virtual double evaluateFunction (const String& functionName,
|
||||
const double* parameters, int numParameters) const;
|
||||
|
||||
/** Used as a callback by the Scope::visitRelativeScope() method.
|
||||
You should never create an instance of this class yourself, it's used by the
|
||||
expression evaluation code.
|
||||
*/
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
virtual ~Visitor() {}
|
||||
virtual void visit (const Scope&) = 0;
|
||||
};
|
||||
|
||||
/** Creates a Scope object for a named scope, and then calls a visitor
|
||||
to do some kind of processing with this new scope.
|
||||
|
||||
If the name is valid, this method must create a suitable (temporary) Scope
|
||||
object to represent it, and must call the Visitor::visit() method with this
|
||||
new scope.
|
||||
*/
|
||||
virtual void visitRelativeScope (const String& scopeName, Visitor& visitor) const;
|
||||
};
|
||||
|
||||
/** Evaluates this expression, without using a Scope.
|
||||
Without a Scope, no symbols can be used, and only basic functions such as sin, cos, tan,
|
||||
min, max are available.
|
||||
To find out about any errors during evaluation, use the other version of this method which
|
||||
takes a String parameter.
|
||||
*/
|
||||
double evaluate() const;
|
||||
|
||||
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols
|
||||
or functions that it uses.
|
||||
To find out about any errors during evaluation, use the other version of this method which
|
||||
takes a String parameter.
|
||||
*/
|
||||
double evaluate (const Scope& scope) const;
|
||||
|
||||
/** Evaluates this expression, providing a scope that should be able to evaluate any symbols
|
||||
or functions that it uses.
|
||||
*/
|
||||
double evaluate (const Scope& scope, String& evaluationError) const;
|
||||
|
||||
/** Attempts to return an expression which is a copy of this one, but with a constant adjusted
|
||||
to make the expression resolve to a target value.
|
||||
|
||||
E.g. if the expression is "x + 10" and x is 5, then asking for a target value of 8 will return
|
||||
the expression "x + 3". Obviously some expressions can't be reversed in this way, in which
|
||||
case they might just be adjusted by adding a constant to the original expression.
|
||||
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
Expression adjustedToGiveNewResult (double targetValue, const Scope& scope) const;
|
||||
|
||||
/** Represents a symbol that is used in an Expression. */
|
||||
struct Symbol
|
||||
{
|
||||
Symbol (const String& scopeUID, const String& symbolName);
|
||||
bool operator== (const Symbol&) const noexcept;
|
||||
bool operator!= (const Symbol&) const noexcept;
|
||||
|
||||
String scopeUID; /**< The unique ID of the Scope that contains this symbol. */
|
||||
String symbolName; /**< The name of the symbol. */
|
||||
};
|
||||
|
||||
/** Returns a copy of this expression in which all instances of a given symbol have been renamed. */
|
||||
Expression withRenamedSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope) const;
|
||||
|
||||
/** Returns true if this expression makes use of the specified symbol.
|
||||
If a suitable scope is supplied, the search will dereference and recursively check
|
||||
all symbols, so that it can be determined whether this expression relies on the given
|
||||
symbol at any level in its evaluation. If the scope parameter is null, this just checks
|
||||
whether the expression contains any direct references to the symbol.
|
||||
|
||||
@throws Expression::EvaluationError
|
||||
*/
|
||||
bool referencesSymbol (const Symbol& symbol, const Scope& scope) const;
|
||||
|
||||
/** Returns true if this expression contains any symbols. */
|
||||
bool usesAnySymbols() const;
|
||||
|
||||
/** Returns a list of all symbols that may be needed to resolve this expression in the given scope. */
|
||||
void findReferencedSymbols (Array<Symbol>& results, const Scope& scope) const;
|
||||
|
||||
//==============================================================================
|
||||
/** An exception that can be thrown by Expression::parse(). */
|
||||
class ParseError : public std::exception
|
||||
{
|
||||
public:
|
||||
ParseError (const String& message);
|
||||
|
||||
String description;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Expression type.
|
||||
@see Expression::getType()
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
constantType,
|
||||
functionType,
|
||||
operatorType,
|
||||
symbolType
|
||||
};
|
||||
|
||||
/** Returns the type of this expression. */
|
||||
Type getType() const noexcept;
|
||||
|
||||
/** If this expression is a symbol, function or operator, this returns its identifier. */
|
||||
String getSymbolOrFunction() const;
|
||||
|
||||
/** Returns the number of inputs to this expression.
|
||||
@see getInput
|
||||
*/
|
||||
int getNumInputs() const;
|
||||
|
||||
/** Retrieves one of the inputs to this expression.
|
||||
@see getNumInputs
|
||||
*/
|
||||
Expression getInput (int index) const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Term;
|
||||
struct Helpers;
|
||||
friend class Term;
|
||||
friend struct Helpers;
|
||||
friend class ScopedPointer<Term>;
|
||||
friend class SharedObjectPtr<Term>;
|
||||
SharedObjectPtr<Term> term;
|
||||
|
||||
explicit Expression (Term*);
|
||||
};
|
||||
|
||||
#endif // BEAST_EXPRESSION_BEASTHEADER
|
||||
@@ -1,387 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_INTERVAL_BEASTHEADER
|
||||
#define BEAST_INTERVAL_BEASTHEADER
|
||||
|
||||
/** A half-open interval.
|
||||
|
||||
This represents the half-open interval [begin, end) over the scalar
|
||||
type of template parameter `Ty`. It may also be considered as the
|
||||
specification of a subset of a 1-dimensional Euclidean space.
|
||||
|
||||
@tparam Ty A scalar numerical type.
|
||||
*/
|
||||
template <class Ty>
|
||||
class Interval
|
||||
{
|
||||
public:
|
||||
typedef Ty value_type;
|
||||
|
||||
/** The empty interval.
|
||||
*/
|
||||
static const Interval none;
|
||||
|
||||
/** Create an uninitialized interval.
|
||||
*/
|
||||
Interval ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Create an interval with the specified values.
|
||||
*/
|
||||
Interval (Ty begin, Ty end)
|
||||
: m_begin (begin)
|
||||
, m_end (end)
|
||||
{
|
||||
}
|
||||
|
||||
/** Create an interval from another interval.
|
||||
*/
|
||||
Interval (Interval const& other)
|
||||
: m_begin (other.m_begin)
|
||||
, m_end (other.m_end)
|
||||
{
|
||||
}
|
||||
|
||||
/** Assign from another interval.
|
||||
|
||||
@param other The interval to assign from.
|
||||
|
||||
@return A reference to this interval.
|
||||
*/
|
||||
Interval& operator= (const Interval& other)
|
||||
{
|
||||
m_begin = other.m_begin;
|
||||
m_end = other.m_end;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Compare an interval for equality.
|
||||
|
||||
Empty intervals are always equal to other empty intervals.
|
||||
|
||||
@param rhs The other interval to compare.
|
||||
|
||||
@return `true` if this interval is equal to the specified interval.
|
||||
*/
|
||||
bool operator== (Interval const& rhs) const
|
||||
{
|
||||
return (empty () && rhs.empty ()) ||
|
||||
(m_begin == rhs.m_begin && m_end == rhs.m_end);
|
||||
}
|
||||
|
||||
/** Compare an interval for inequality.
|
||||
|
||||
@param rhs The other interval to compare.
|
||||
|
||||
@return `true` if this interval is not equal to the specified interval.
|
||||
*/
|
||||
bool operator!= (Interval const& rhs) const
|
||||
{
|
||||
return !this->operator== (rhs);
|
||||
}
|
||||
|
||||
/** Get the starting value of the interval.
|
||||
|
||||
@return The starting point of the interval.
|
||||
*/
|
||||
Ty begin () const
|
||||
{
|
||||
return m_begin;
|
||||
}
|
||||
|
||||
/** Get the ending value of the interval.
|
||||
|
||||
@return The ending point of the interval.
|
||||
*/
|
||||
Ty end () const
|
||||
{
|
||||
return m_end;
|
||||
}
|
||||
|
||||
/** Get the Lebesque measure.
|
||||
|
||||
@return The Lebesque measure.
|
||||
*/
|
||||
Ty length () const
|
||||
{
|
||||
return empty () ? Ty () : (end () - begin ());
|
||||
}
|
||||
|
||||
//Ty count () const { return length (); } // sugar
|
||||
//Ty distance () const { return length (); } // sugar
|
||||
|
||||
/** Determine if the interval is empty.
|
||||
|
||||
@return `true` if the interval is empty.
|
||||
*/
|
||||
bool empty () const
|
||||
{
|
||||
return m_begin >= m_end;
|
||||
}
|
||||
|
||||
/** Determine if the interval is non-empty.
|
||||
|
||||
@return `true` if the interval is not empty.
|
||||
*/
|
||||
bool notEmpty () const
|
||||
{
|
||||
return m_begin < m_end;
|
||||
}
|
||||
|
||||
/** Set the starting point of the interval.
|
||||
|
||||
@param v The starting point.
|
||||
*/
|
||||
void setBegin (Ty v)
|
||||
{
|
||||
m_begin = v;
|
||||
}
|
||||
|
||||
/** Set the ending point of the interval.
|
||||
|
||||
@param v The ending point.
|
||||
*/
|
||||
void setEnd (Ty v)
|
||||
{
|
||||
m_end = v;
|
||||
}
|
||||
|
||||
/** Set the ending point relative to the starting point.
|
||||
|
||||
@param v The length of the resulting interval.
|
||||
*/
|
||||
void setLength (Ty v)
|
||||
{
|
||||
m_end = m_begin + v;
|
||||
}
|
||||
|
||||
/** Determine if a value is contained in the interval.
|
||||
|
||||
@param v The value to check.
|
||||
|
||||
@return `true` if this interval contains `v`.
|
||||
*/
|
||||
bool contains (Ty v) const
|
||||
{
|
||||
return notEmpty () && v >= m_begin && v < m_end;
|
||||
}
|
||||
|
||||
/** Determine if this interval intersects another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if the intervals intersect.
|
||||
*/
|
||||
template <class To>
|
||||
bool intersects (Interval <To> const& other) const
|
||||
{
|
||||
return notEmpty () && other.notEmpty () &&
|
||||
end () > other.begin () && begin () < other.end ();
|
||||
}
|
||||
|
||||
/** Determine if this interval adjoins another interval.
|
||||
|
||||
An interval is adjoint to another interval if and only if the union of the
|
||||
intervals is a single non-empty half-open subset.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if the intervals are adjoint.
|
||||
*/
|
||||
template <class To>
|
||||
bool adjoins (Interval <To> const& other) const
|
||||
{
|
||||
return (empty () != other.empty ()) ||
|
||||
(notEmpty () && end () >= other.begin ()
|
||||
&& begin () <= other.end ());
|
||||
}
|
||||
|
||||
/** Determine if this interval is disjoint from another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if the intervals are disjoint.
|
||||
*/
|
||||
bool disjoint (Interval const& other) const
|
||||
{
|
||||
return !intersects (other);
|
||||
}
|
||||
|
||||
/** Determine if this interval is a superset of another interval.
|
||||
|
||||
An interval A is a superset of interval B if B is empty or if A fully
|
||||
contains B.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this is a superset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool superset_of (Interval <To> const& other) const
|
||||
{
|
||||
return other.empty () ||
|
||||
(notEmpty () && begin () <= other.begin ()
|
||||
&& end () >= other.end ());
|
||||
}
|
||||
|
||||
/** Determine if this interval is a proper superset of another interval.
|
||||
|
||||
An interval A is a proper superset of interval B if A is a superset of
|
||||
B and A is not equal to B.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this interval is a proper superset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool proper_superset_of (Interval <To> const& other) const
|
||||
{
|
||||
return this->superset_of (other) && this->operator != (other);
|
||||
}
|
||||
|
||||
/** Determine if this interval is a subset of another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this interval is a subset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool subset_of (Interval <To> const& other) const
|
||||
{
|
||||
return other.superset_of (*this);
|
||||
}
|
||||
|
||||
/** Determine if this interval is a proper subset of another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return `true` if this interval is a proper subset of `other`.
|
||||
*/
|
||||
template <class To>
|
||||
bool proper_subset_of (Interval <To> const& other) const
|
||||
{
|
||||
return other.proper_superset_of (*this);
|
||||
}
|
||||
|
||||
/** Return the intersection of this interval with another interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return The intersection of the intervals.
|
||||
*/
|
||||
template <class To>
|
||||
Interval intersection (Interval <To> const& other) const
|
||||
{
|
||||
return Interval (std::max (begin (), other.begin ()),
|
||||
std::min (end (), other.end ()));
|
||||
}
|
||||
|
||||
/** Determine the smallest interval that contains both intervals.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return The simple union of the intervals.
|
||||
*/
|
||||
template <class To>
|
||||
Interval simple_union (Interval <To> const& other) const
|
||||
{
|
||||
return Interval (
|
||||
std::min (other.normalized ().begin (), normalized ().begin ()),
|
||||
std::max (other.normalized ().end (), normalized ().end ()));
|
||||
}
|
||||
|
||||
/** Calculate the single-interval union.
|
||||
|
||||
The result is empty if the union cannot be represented as a
|
||||
single half-open interval.
|
||||
|
||||
@param other The other interval.
|
||||
|
||||
@return The simple union of the intervals.
|
||||
*/
|
||||
template <class To>
|
||||
Interval single_union (Interval <To> const& other) const
|
||||
{
|
||||
if (empty ())
|
||||
return other;
|
||||
|
||||
else if (other.empty ())
|
||||
return *this;
|
||||
|
||||
else if (end () < other.begin () || begin () > other.end ())
|
||||
return none;
|
||||
|
||||
else
|
||||
return Interval (std::min (begin (), other.begin ()),
|
||||
std::max (end (), other.end ()));
|
||||
}
|
||||
|
||||
/** Determine if the interval is correctly ordered.
|
||||
|
||||
@return `true` if the interval is correctly ordered.
|
||||
*/
|
||||
bool normal () const
|
||||
{
|
||||
return end () >= begin ();
|
||||
}
|
||||
|
||||
/** Return a normalized interval.
|
||||
|
||||
@return The normalized interval.
|
||||
*/
|
||||
Interval normalized () const
|
||||
{
|
||||
if (normal ())
|
||||
return *this;
|
||||
else
|
||||
return Interval (end (), begin ());
|
||||
}
|
||||
|
||||
/** Clamp a value to the interval.
|
||||
|
||||
@param v The value to clamp.
|
||||
|
||||
@return The clamped result.
|
||||
*/
|
||||
template <typename Tv>
|
||||
Ty clamp (Tv v) const
|
||||
{
|
||||
// These conditionals are carefully ordered so
|
||||
// that if m_begin == m_end, value is assigned m_begin.
|
||||
if (v > end ())
|
||||
v = end () - (std::numeric_limits <Tv>::is_integer ? 1 :
|
||||
std::numeric_limits <Tv>::epsilon ());
|
||||
|
||||
if (v < begin ())
|
||||
v = begin ();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
Ty m_begin;
|
||||
Ty m_end;
|
||||
};
|
||||
|
||||
template <typename Ty>
|
||||
const Interval<Ty> Interval<Ty>::none = Interval<Ty> (Ty (), Ty ());
|
||||
|
||||
#endif
|
||||
@@ -1,83 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICCOUNTER_BEASTHEADER
|
||||
#define BEAST_ATOMICCOUNTER_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe usage counter.
|
||||
|
||||
This provides a simplified interface to an atomic integer suitable for
|
||||
measuring reference or usage counts. The counter is signaled when the
|
||||
count is non zero.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API AtomicCounter
|
||||
{
|
||||
public:
|
||||
/** Create a new counter.
|
||||
|
||||
@param initialValue An optional starting usage count (default is 0).
|
||||
*/
|
||||
AtomicCounter (int initialValue = 0) noexcept
|
||||
:
|
||||
m_value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
/** Increment the usage count.
|
||||
|
||||
@return `true` if the counter became signaled.
|
||||
*/
|
||||
inline bool addref () noexcept
|
||||
{
|
||||
return (++m_value) == 1;
|
||||
}
|
||||
|
||||
/** Decrements the usage count.
|
||||
|
||||
@return `true` if the counter became non-signaled.
|
||||
*/
|
||||
inline bool release () noexcept
|
||||
{
|
||||
// Unfortunately, AllocatorWithoutTLS breaks this assert
|
||||
//bassert (isSignaled ());
|
||||
|
||||
return (--m_value) == 0;
|
||||
}
|
||||
|
||||
/** Determine if the counter is signaled.
|
||||
|
||||
Note that another thread can cause the counter to become reset after
|
||||
this function returns true.
|
||||
|
||||
@return `true` if the counter was signaled.
|
||||
*/
|
||||
inline bool isSignaled () const noexcept
|
||||
{
|
||||
return m_value.get () > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <int> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,102 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICFLAG_BEASTHEADER
|
||||
#define BEAST_ATOMICFLAG_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe flag.
|
||||
|
||||
This provides a simplified interface to an atomic integer suitable for
|
||||
representing a flag. The flag is signaled when on, else it is considered
|
||||
reset.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API AtomicFlag
|
||||
{
|
||||
public:
|
||||
/** Create an AtomicFlag in the reset state. */
|
||||
AtomicFlag () noexcept
|
||||
:
|
||||
m_value (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Signal the flag.
|
||||
|
||||
If two or more threads simultaneously attempt to signal the flag,
|
||||
only one will receive a true return value.
|
||||
|
||||
@return true if the flag was previously reset.
|
||||
*/
|
||||
inline bool trySignal () noexcept
|
||||
{
|
||||
return m_value.compareAndSetBool (1, 0);
|
||||
}
|
||||
|
||||
/** Signal the flag.
|
||||
|
||||
The flag must be in the reset state. Only one thread may
|
||||
call this at a time.
|
||||
*/
|
||||
inline void signal () noexcept
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
const bool success = m_value.compareAndSetBool (1, 0);
|
||||
bassert (success);
|
||||
#else
|
||||
m_value.set (1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Reset the flag.
|
||||
|
||||
The flag must be in the signaled state. Only one thread may
|
||||
call this at a time. Usually it is the thread that was successful
|
||||
in a previous call to trySignal().
|
||||
*/
|
||||
inline void reset () noexcept
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
const bool success = m_value.compareAndSetBool (0, 1);
|
||||
bassert (success);
|
||||
#else
|
||||
m_value.set (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Check if the AtomicFlag is signaled
|
||||
|
||||
The signaled status may change immediately after this call
|
||||
returns. The caller must synchronize.
|
||||
|
||||
@return true if the flag was signaled.
|
||||
*/
|
||||
inline bool isSignaled () const noexcept
|
||||
{
|
||||
return m_value.get () == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <int> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,133 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICPOINTER_BEASTHEADER
|
||||
#define BEAST_ATOMICPOINTER_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe pointer.
|
||||
|
||||
This provides a simplified interface to an atomic pointer suitable
|
||||
for building containers or composite classes. Operator overloads
|
||||
allow access to the underlying pointer using natural C++ syntax.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
template <class P>
|
||||
class AtomicPointer
|
||||
{
|
||||
public:
|
||||
/** Create a pointer.
|
||||
|
||||
@param initialValue An optional starting value (default is null).
|
||||
*/
|
||||
explicit AtomicPointer (P* const initialValue = nullptr) noexcept
|
||||
:
|
||||
m_value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
/** Retrieve the pointer value */
|
||||
inline P* get () const noexcept
|
||||
{
|
||||
return m_value.get ();
|
||||
}
|
||||
|
||||
/** Obtain a pointer to P through type conversion.
|
||||
|
||||
The caller must synchronize access to P.
|
||||
|
||||
@return A pointer to P.
|
||||
*/
|
||||
inline operator P* () const noexcept
|
||||
{
|
||||
return get ();
|
||||
}
|
||||
|
||||
/** Dereference operator
|
||||
|
||||
The caller must synchronize access to P.
|
||||
|
||||
@return A reference to P.
|
||||
*/
|
||||
inline P& operator* () const noexcept
|
||||
{
|
||||
return &get ();
|
||||
}
|
||||
|
||||
/** Member selection
|
||||
|
||||
The caller must synchronize access to P.
|
||||
|
||||
@return A pointer to P.
|
||||
*/
|
||||
inline P* operator-> () const noexcept
|
||||
{
|
||||
return get ();
|
||||
}
|
||||
|
||||
inline void set (P* p)
|
||||
{
|
||||
m_value.set (p);
|
||||
}
|
||||
|
||||
/** Atomically assign a new pointer
|
||||
|
||||
@param newValue The new value to assign.
|
||||
*/
|
||||
inline void operator= (P* newValue) noexcept
|
||||
{
|
||||
set (newValue);
|
||||
}
|
||||
|
||||
/** Atomically assign a new pointer and return the old value.
|
||||
|
||||
@param newValue The new value to assign.
|
||||
|
||||
@return The previous value.
|
||||
*/
|
||||
inline P* exchange (P* newValue)
|
||||
{
|
||||
return m_value.exchange (newValue);
|
||||
}
|
||||
|
||||
/** Conditionally perform an atomic assignment.
|
||||
|
||||
The current value is compared with oldValue and atomically
|
||||
set to newValue if the comparison is equal.
|
||||
|
||||
The caller is responsible for handling the ABA problem.
|
||||
|
||||
@param newValue The new value to assign.
|
||||
|
||||
@param oldValue The matching old value.
|
||||
|
||||
@return true if the assignment was performed.
|
||||
*/
|
||||
inline bool compareAndSet (P* newValue, P* oldValue)
|
||||
{
|
||||
return m_value.compareAndSetBool (newValue, oldValue);
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <P*> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_ATOMICSTATE_BEASTHEADER
|
||||
#define BEAST_ATOMICSTATE_BEASTHEADER
|
||||
|
||||
/*============================================================================*/
|
||||
/**
|
||||
A thread safe state variable.
|
||||
|
||||
This provides a simplified interface to an integer used to control atomic
|
||||
state transitions. A state is distinguished by a single integer value.
|
||||
|
||||
@ingroup beast_core
|
||||
*/
|
||||
class BEAST_API AtomicState
|
||||
{
|
||||
public:
|
||||
/** Create a new state with an optional starting value.
|
||||
|
||||
@param initialState The initial state.
|
||||
*/
|
||||
|
||||
|
||||
explicit AtomicState (const int initialState = 0) noexcept
|
||||
:
|
||||
m_value (initialState)
|
||||
{
|
||||
}
|
||||
|
||||
/** Retrieve the current state.
|
||||
|
||||
This converts the object to an integer reflecting the current state.
|
||||
|
||||
Note that other threads may change the value immediately after this
|
||||
function returns. The caller is responsible for synchronizing.
|
||||
|
||||
@return The state at the time of the call.
|
||||
*/
|
||||
inline operator int () const
|
||||
{
|
||||
return m_value.get ();
|
||||
}
|
||||
|
||||
/** Attempt a state transition.
|
||||
|
||||
The current state is compared to `from`, and if the comparison is
|
||||
successful the state becomes `to`. The entire operation is atomic.
|
||||
|
||||
@param from The current state, for comparison.
|
||||
|
||||
@param to The desired new state.
|
||||
|
||||
@return true if the state transition succeeded.
|
||||
*/
|
||||
inline bool tryChangeState (const int from, const int to) noexcept
|
||||
{
|
||||
return m_value.compareAndSetBool (to, from);
|
||||
}
|
||||
|
||||
/** Perform a state transition.
|
||||
|
||||
This attempts to change the state and generates a diagnostic on
|
||||
failure. This routine can be used instead of tryChangeState()
|
||||
when program logic requires that the state change must succeed.
|
||||
|
||||
@param from The required current state.
|
||||
|
||||
@param to The new state.
|
||||
*/
|
||||
inline void changeState (const int from, const int to) noexcept
|
||||
{
|
||||
#if BEAST_DEBUG
|
||||
const bool success = tryChangeState (from, to);
|
||||
bassert (success);
|
||||
#else
|
||||
tryChangeState (from, to);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic <int> m_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,126 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_BYTESWAP_H_INCLUDED
|
||||
#define BEAST_BYTESWAP_H_INCLUDED
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/** Specialized helper class template for swapping bytes.
|
||||
|
||||
Normally you won't use this directly, use the helper function
|
||||
byteSwap instead. You can specialize this class for your
|
||||
own user defined types, as was done for uint24.
|
||||
|
||||
@see swapBytes, uint24
|
||||
*/
|
||||
template <typename IntegralType>
|
||||
struct SwapBytes
|
||||
{
|
||||
inline IntegralType operator() (IntegralType value) const noexcept
|
||||
{
|
||||
return ByteOrder::swap (value);
|
||||
}
|
||||
};
|
||||
|
||||
// Specializations for signed integers
|
||||
|
||||
template <>
|
||||
struct SwapBytes <int16>
|
||||
{
|
||||
inline int16 operator() (int16 value) const noexcept
|
||||
{
|
||||
return static_cast <int16> (ByteOrder::swap (static_cast <uint16> (value)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SwapBytes <int32>
|
||||
{
|
||||
inline int32 operator() (int32 value) const noexcept
|
||||
{
|
||||
return static_cast <int32> (ByteOrder::swap (static_cast <uint32> (value)));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SwapBytes <int64>
|
||||
{
|
||||
inline int64 operator() (int64 value) const noexcept
|
||||
{
|
||||
return static_cast <int64> (ByteOrder::swap (static_cast <uint64> (value)));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Returns a type with the bytes swapped.
|
||||
Little endian becomes big endian and vice versa. The underlying
|
||||
type must be an integral type or behave like one.
|
||||
*/
|
||||
template <class IntegralType>
|
||||
inline IntegralType swapBytes (IntegralType value) noexcept
|
||||
{
|
||||
return detail::SwapBytes <IntegralType> () (value);
|
||||
}
|
||||
|
||||
/** Returns the machine byte-order value to little-endian byte order. */
|
||||
template <typename IntegralType>
|
||||
inline IntegralType toLittleEndian (IntegralType value) noexcept
|
||||
{
|
||||
#if BEAST_LITTLE_ENDIAN
|
||||
return value;
|
||||
#else
|
||||
return swapBytes (value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns the machine byte-order value to big-endian byte order. */
|
||||
template <typename IntegralType>
|
||||
inline IntegralType toBigEndian (IntegralType value) noexcept
|
||||
{
|
||||
#if BEAST_LITTLE_ENDIAN
|
||||
return swapBytes (value);
|
||||
#else
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Returns the machine byte-order value to network byte order. */
|
||||
template <typename IntegralType>
|
||||
inline IntegralType toNetworkByteOrder (IntegralType value) noexcept
|
||||
{
|
||||
return toBigEndian (value);
|
||||
}
|
||||
|
||||
/** Converts from network byte order to machine byte order. */
|
||||
template <typename IntegralType>
|
||||
inline IntegralType fromNetworkByteOrder (IntegralType value) noexcept
|
||||
{
|
||||
#if BEAST_LITTLE_ENDIAN
|
||||
return swapBytes (value);
|
||||
#else
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,126 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_RECYCLEDOBJECTPOOL_H_INCLUDED
|
||||
#define BEAST_RECYCLEDOBJECTPOOL_H_INCLUDED
|
||||
|
||||
/** A pool of objects which may be recycled.
|
||||
|
||||
This is a thread safe pool of objects that get re-used. It is
|
||||
primarily designed to eliminate the need for many memory allocations
|
||||
and frees when temporary buffers are needed for operations.
|
||||
|
||||
To use it, first declare a structure containing the information
|
||||
that you want to recycle. Then when you want to use a recycled object
|
||||
put a ScopedItem on your stack:
|
||||
|
||||
@code
|
||||
|
||||
struct StdString
|
||||
{
|
||||
std::string data;
|
||||
};
|
||||
|
||||
RecycledObjectPool <StdString> pool;
|
||||
|
||||
void foo ()
|
||||
{
|
||||
RecycledObjectPool <StdString>::ScopedItem item;
|
||||
|
||||
item.getObject ().data = "text";
|
||||
}
|
||||
|
||||
@endcode
|
||||
*/
|
||||
template <class Object>
|
||||
class RecycledObjectPool
|
||||
{
|
||||
public:
|
||||
struct Item : Object, LockFreeStack <Item>::Node, LeakChecked <Item>
|
||||
{
|
||||
};
|
||||
|
||||
class ScopedItem
|
||||
{
|
||||
public:
|
||||
explicit ScopedItem (RecycledObjectPool <Object>& pool)
|
||||
: m_pool (pool)
|
||||
, m_item (pool.get ())
|
||||
{
|
||||
}
|
||||
|
||||
~ScopedItem ()
|
||||
{
|
||||
m_pool.release (m_item);
|
||||
}
|
||||
|
||||
Object& getObject () noexcept
|
||||
{
|
||||
return *m_item;
|
||||
}
|
||||
|
||||
private:
|
||||
RecycledObjectPool <Object>& m_pool;
|
||||
Item* const m_item;
|
||||
};
|
||||
|
||||
public:
|
||||
RecycledObjectPool () noexcept
|
||||
{
|
||||
}
|
||||
|
||||
~RecycledObjectPool ()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
Item* const item = m_stack.pop_front ();
|
||||
|
||||
if (item != nullptr)
|
||||
delete item;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Item* get ()
|
||||
{
|
||||
Item* item = m_stack.pop_front ();
|
||||
|
||||
if (item == nullptr)
|
||||
{
|
||||
item = new Item;
|
||||
|
||||
if (item == nullptr)
|
||||
Throw (std::bad_alloc ());
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void release (Item* item) noexcept
|
||||
{
|
||||
m_stack.push_front (item);
|
||||
}
|
||||
|
||||
private:
|
||||
LockFreeStack <Item> m_stack;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,390 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_REFERENCECOUNTEDOBJECT_BEASTHEADER
|
||||
#define BEAST_REFERENCECOUNTEDOBJECT_BEASTHEADER
|
||||
|
||||
#include "beast_Atomic.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Adds reference-counting to an object.
|
||||
|
||||
To add reference-counting to a class, derive it from this class, and
|
||||
use the SharedObjectPtr class to point to it.
|
||||
|
||||
e.g. @code
|
||||
class MyClass : public SharedObject
|
||||
{
|
||||
void foo();
|
||||
|
||||
// This is a neat way of declaring a typedef for a pointer class,
|
||||
// rather than typing out the full templated name each time..
|
||||
typedef SharedObjectPtr<MyClass> Ptr;
|
||||
};
|
||||
|
||||
MyClass::Ptr p = new MyClass();
|
||||
MyClass::Ptr p2 = p;
|
||||
p = nullptr;
|
||||
p2->foo();
|
||||
@endcode
|
||||
|
||||
Once a new SharedObject has been assigned to a pointer, be
|
||||
careful not to delete the object manually.
|
||||
|
||||
This class uses an Atomic<int> value to hold the reference count, so that it
|
||||
the pointers can be passed between threads safely. For a faster but non-thread-safe
|
||||
version, use SingleThreadedSharedObject instead.
|
||||
|
||||
@see SharedObjectPtr, SharedObjectArray, SingleThreadedSharedObject
|
||||
*/
|
||||
class BEAST_API SharedObject : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Increments the object's reference count.
|
||||
|
||||
This is done automatically by the smart pointer, but is public just
|
||||
in case it's needed for nefarious purposes.
|
||||
*/
|
||||
inline void incReferenceCount() noexcept
|
||||
{
|
||||
++refCount;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
|
||||
If the count gets to zero, the object will be deleted.
|
||||
*/
|
||||
inline void decReferenceCount() noexcept
|
||||
{
|
||||
bassert (getReferenceCount() > 0);
|
||||
|
||||
if (--refCount == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
/** Returns the object's current reference count. */
|
||||
inline int getReferenceCount() const noexcept { return refCount.get(); }
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the reference-counted object (with an initial ref count of zero). */
|
||||
SharedObject()
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SharedObject()
|
||||
{
|
||||
// it's dangerous to delete an object that's still referenced by something else!
|
||||
bassert (getReferenceCount() == 0);
|
||||
}
|
||||
|
||||
/** Resets the reference count to zero without deleting the object.
|
||||
You should probably never need to use this!
|
||||
*/
|
||||
void resetReferenceCount() noexcept
|
||||
{
|
||||
refCount = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Atomic <int> refCount;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Adds reference-counting to an object.
|
||||
|
||||
This is effectively a version of the SharedObject class, but which
|
||||
uses a non-atomic counter, and so is not thread-safe (but which will be more
|
||||
efficient).
|
||||
For more details on how to use it, see the SharedObject class notes.
|
||||
|
||||
@see SharedObject, SharedObjectPtr, SharedObjectArray
|
||||
*/
|
||||
class BEAST_API SingleThreadedSharedObject : public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Increments the object's reference count.
|
||||
|
||||
This is done automatically by the smart pointer, but is public just
|
||||
in case it's needed for nefarious purposes.
|
||||
*/
|
||||
inline void incReferenceCount() noexcept
|
||||
{
|
||||
++refCount;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
|
||||
If the count gets to zero, the object will be deleted.
|
||||
*/
|
||||
inline void decReferenceCount() noexcept
|
||||
{
|
||||
bassert (getReferenceCount() > 0);
|
||||
|
||||
if (--refCount == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
/** Returns the object's current reference count. */
|
||||
inline int getReferenceCount() const noexcept { return refCount; }
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the reference-counted object (with an initial ref count of zero). */
|
||||
SingleThreadedSharedObject() : refCount (0) {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SingleThreadedSharedObject()
|
||||
{
|
||||
// it's dangerous to delete an object that's still referenced by something else!
|
||||
bassert (getReferenceCount() == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int refCount;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A smart-pointer class which points to a reference-counted object.
|
||||
|
||||
The template parameter specifies the class of the object you want to point to - the easiest
|
||||
way to make a class reference-countable is to simply make it inherit from SharedObject,
|
||||
but if you need to, you could roll your own reference-countable class by implementing a pair of
|
||||
mathods called incReferenceCount() and decReferenceCount().
|
||||
|
||||
When using this class, you'll probably want to create a typedef to abbreviate the full
|
||||
templated name - e.g.
|
||||
@code typedef SharedObjectPtr<MyClass> MyClassPtr;@endcode
|
||||
|
||||
@see SharedObject, SharedObjectArray
|
||||
*/
|
||||
template <class SharedObjectClass>
|
||||
class SharedObjectPtr
|
||||
{
|
||||
public:
|
||||
/** The class being referenced by this pointer. */
|
||||
typedef SharedObjectClass ReferencedType;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a pointer to a null object. */
|
||||
inline SharedObjectPtr() noexcept
|
||||
: referencedObject (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a pointer to an object.
|
||||
|
||||
This will increment the object's reference-count if it is non-null.
|
||||
*/
|
||||
inline SharedObjectPtr (SharedObjectClass* const refCountedObject) noexcept
|
||||
: referencedObject (refCountedObject)
|
||||
{
|
||||
if (refCountedObject != nullptr)
|
||||
refCountedObject->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Copies another pointer.
|
||||
This will increment the object's reference-count (if it is non-null).
|
||||
*/
|
||||
inline SharedObjectPtr (const SharedObjectPtr& other) noexcept
|
||||
: referencedObject (other.referencedObject)
|
||||
{
|
||||
if (referencedObject != nullptr)
|
||||
referencedObject->incReferenceCount();
|
||||
}
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
/** Takes-over the object from another pointer. */
|
||||
inline SharedObjectPtr (SharedObjectPtr&& other) noexcept
|
||||
: referencedObject (other.referencedObject)
|
||||
{
|
||||
other.referencedObject = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Copies another pointer.
|
||||
This will increment the object's reference-count (if it is non-null).
|
||||
*/
|
||||
template <class DerivedClass>
|
||||
inline SharedObjectPtr (const SharedObjectPtr<DerivedClass>& other) noexcept
|
||||
: referencedObject (static_cast <SharedObjectClass*> (other.get()))
|
||||
{
|
||||
if (referencedObject != nullptr)
|
||||
referencedObject->incReferenceCount();
|
||||
}
|
||||
|
||||
/** Changes this pointer to point at a different object.
|
||||
|
||||
The reference count of the old object is decremented, and it might be
|
||||
deleted if it hits zero. The new object's count is incremented.
|
||||
*/
|
||||
SharedObjectPtr& operator= (const SharedObjectPtr& other)
|
||||
{
|
||||
return operator= (other.referencedObject);
|
||||
}
|
||||
|
||||
/** Changes this pointer to point at a different object.
|
||||
|
||||
The reference count of the old object is decremented, and it might be
|
||||
deleted if it hits zero. The new object's count is incremented.
|
||||
*/
|
||||
template <class DerivedClass>
|
||||
SharedObjectPtr& operator= (const SharedObjectPtr<DerivedClass>& other)
|
||||
{
|
||||
return operator= (static_cast <SharedObjectClass*> (other.get()));
|
||||
}
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
/** Takes-over the object from another pointer. */
|
||||
SharedObjectPtr& operator= (SharedObjectPtr&& other)
|
||||
{
|
||||
std::swap (referencedObject, other.referencedObject);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Changes this pointer to point at a different object.
|
||||
|
||||
The reference count of the old object is decremented, and it might be
|
||||
deleted if it hits zero. The new object's count is incremented.
|
||||
*/
|
||||
SharedObjectPtr& operator= (SharedObjectClass* const newObject)
|
||||
{
|
||||
if (referencedObject != newObject)
|
||||
{
|
||||
if (newObject != nullptr)
|
||||
newObject->incReferenceCount();
|
||||
|
||||
SharedObjectClass* const oldObject = referencedObject;
|
||||
referencedObject = newObject;
|
||||
|
||||
if (oldObject != nullptr)
|
||||
oldObject->decReferenceCount();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
|
||||
This will decrement the object's reference-count, and may delete it if it
|
||||
gets to zero.
|
||||
*/
|
||||
inline ~SharedObjectPtr()
|
||||
{
|
||||
if (referencedObject != nullptr)
|
||||
referencedObject->decReferenceCount();
|
||||
}
|
||||
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be zero, of course.
|
||||
*/
|
||||
inline operator SharedObjectClass*() const noexcept
|
||||
{
|
||||
return referencedObject;
|
||||
}
|
||||
|
||||
// the -> operator is called on the referenced object
|
||||
inline SharedObjectClass* operator->() const noexcept
|
||||
{
|
||||
return referencedObject;
|
||||
}
|
||||
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be zero, of course.
|
||||
*/
|
||||
inline SharedObjectClass* get() const noexcept
|
||||
{
|
||||
return referencedObject;
|
||||
}
|
||||
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be zero, of course.
|
||||
*/
|
||||
inline SharedObjectClass* getObject() const noexcept
|
||||
{
|
||||
return referencedObject;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SharedObjectClass* referencedObject;
|
||||
};
|
||||
|
||||
|
||||
/** Compares two SharedObjectPointers. */
|
||||
template <class SharedObjectClass>
|
||||
bool operator== (const SharedObjectPtr<SharedObjectClass>& object1, SharedObjectClass* const object2) noexcept
|
||||
{
|
||||
return object1.get() == object2;
|
||||
}
|
||||
|
||||
/** Compares two SharedObjectPointers. */
|
||||
template <class SharedObjectClass>
|
||||
bool operator== (const SharedObjectPtr<SharedObjectClass>& object1, const SharedObjectPtr<SharedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1.get() == object2.get();
|
||||
}
|
||||
|
||||
/** Compares two SharedObjectPointers. */
|
||||
template <class SharedObjectClass>
|
||||
bool operator== (SharedObjectClass* object1, SharedObjectPtr<SharedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1 == object2.get();
|
||||
}
|
||||
|
||||
/** Compares two SharedObjectPointers. */
|
||||
template <class SharedObjectClass>
|
||||
bool operator!= (const SharedObjectPtr<SharedObjectClass>& object1, const SharedObjectClass* object2) noexcept
|
||||
{
|
||||
return object1.get() != object2;
|
||||
}
|
||||
|
||||
/** Compares two SharedObjectPointers. */
|
||||
template <class SharedObjectClass>
|
||||
bool operator!= (const SharedObjectPtr<SharedObjectClass>& object1, SharedObjectPtr<SharedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1.get() != object2.get();
|
||||
}
|
||||
|
||||
/** Compares two SharedObjectPointers. */
|
||||
template <class SharedObjectClass>
|
||||
bool operator!= (SharedObjectClass* object1, SharedObjectPtr<SharedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1 != object2.get();
|
||||
}
|
||||
|
||||
|
||||
#endif // BEAST_REFERENCECOUNTEDOBJECT_BEASTHEADER
|
||||
@@ -1,172 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_STATICOBJECT_BEASTHEADER
|
||||
#define BEAST_STATICOBJECT_BEASTHEADER
|
||||
|
||||
//
|
||||
// A full suite of thread-safe objects designed for static storage duration.
|
||||
//
|
||||
// Wraps an object with a thread-safe initialization preamble so that it can
|
||||
// properly exist with static storage duration.
|
||||
//
|
||||
// Implementation notes:
|
||||
//
|
||||
// This is accomplished by omitting the constructor and relying on the C++
|
||||
// specification that plain data types with static storage duration are filled
|
||||
// with zeroes before any other initialization code executes.
|
||||
//
|
||||
// Spec: N2914=09-0104
|
||||
//
|
||||
// [3.6.2] Initialization of non-local objects
|
||||
//
|
||||
// Objects with static storage duration (3.7.1) or thread storage
|
||||
// duration (3.7.2) shall be zero-initialized (8.5) before any
|
||||
// other initialization takes place.
|
||||
//
|
||||
// Requirements:
|
||||
//
|
||||
// Object must be constructible without parameters.
|
||||
// The StaticObject must be declared with static storage duration or
|
||||
// the behavior is undefined.
|
||||
//
|
||||
// Usage example:
|
||||
//
|
||||
// Object* getInstance ()
|
||||
// {
|
||||
// static StaticObject <Object> instance;
|
||||
// return instance->getObject ();
|
||||
// }
|
||||
//
|
||||
|
||||
namespace Static
|
||||
{
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Holds an object with static storage duration.
|
||||
// The owner determines if and when the object is constructed and destroyed.
|
||||
// Caller is responsible for synchronization.
|
||||
//
|
||||
template <class ObjectType, class Tag>
|
||||
class Storage
|
||||
{
|
||||
public:
|
||||
static inline void construct ()
|
||||
{
|
||||
new (getObjectPtr ()) ObjectType;
|
||||
}
|
||||
|
||||
static inline void destroy ()
|
||||
{
|
||||
getObjectPtr ()->~ObjectType ();
|
||||
}
|
||||
|
||||
static inline ObjectType* getObjectPtr ()
|
||||
{
|
||||
return reinterpret_cast <ObjectType*> (s_storage);
|
||||
}
|
||||
|
||||
static inline ObjectType& getObject ()
|
||||
{
|
||||
return *getObjectPtr ();
|
||||
}
|
||||
|
||||
inline ObjectType* operator-> () const
|
||||
{
|
||||
return getObjectPtr ();
|
||||
}
|
||||
|
||||
inline ObjectType& operator* () const
|
||||
{
|
||||
return getObject ();
|
||||
}
|
||||
|
||||
inline operator ObjectType* () const
|
||||
{
|
||||
return getObjectPtr ();
|
||||
}
|
||||
|
||||
// TODO: Crashes on iOS if not accessed before usage
|
||||
static char s_storage [sizeof (ObjectType)];
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
template <class ObjectType, class Tag>
|
||||
char Storage <ObjectType, Tag>::s_storage [sizeof (ObjectType)];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Provides a thread safe flag for indicating if and when
|
||||
// initialization is required for an object with static storage duration.
|
||||
//
|
||||
class Initializer
|
||||
{
|
||||
public:
|
||||
// If the condition is not initialized, the first caller will
|
||||
// receive true, while concurrent callers get blocked until
|
||||
// initialization completes.
|
||||
//
|
||||
bool beginConstruction ()
|
||||
{
|
||||
bool needsInitialization = false;
|
||||
|
||||
if (m_state.get () != stateInitialized)
|
||||
{
|
||||
if (m_state.compareAndSetBool (stateInitializing, stateUninitialized))
|
||||
{
|
||||
needsInitialization = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpinDelay delay;
|
||||
|
||||
do
|
||||
{
|
||||
delay.pause ();
|
||||
}
|
||||
while (m_state.get () != stateInitialized);
|
||||
}
|
||||
}
|
||||
|
||||
return needsInitialization;
|
||||
}
|
||||
|
||||
// Called to signal that the initialization is complete
|
||||
//
|
||||
void endConstruction ()
|
||||
{
|
||||
m_state.set (stateInitialized);
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
stateUninitialized = 0, // must be zero
|
||||
stateInitializing,
|
||||
stateInitialized
|
||||
};
|
||||
|
||||
Atomic <int> m_state;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,207 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEAKREFERENCE_BEASTHEADER
|
||||
#define BEAST_WEAKREFERENCE_BEASTHEADER
|
||||
|
||||
#include "beast_SharedObject.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a pointer which will automatically become null if the object
|
||||
to which it points is deleted.
|
||||
|
||||
To accomplish this, the source object needs to cooperate by performing a couple of simple tasks.
|
||||
It must embed a WeakReference::Master object, which stores a shared pointer object, and must clear
|
||||
this master pointer in its destructor.
|
||||
|
||||
E.g.
|
||||
@code
|
||||
class MyObject
|
||||
{
|
||||
public:
|
||||
MyObject()
|
||||
{
|
||||
// If you're planning on using your WeakReferences in a multi-threaded situation, you may choose
|
||||
// to create a WeakReference to the object here in the constructor, which will pre-initialise the
|
||||
// embedded object, avoiding an (extremely unlikely) race condition that could occur if multiple
|
||||
// threads overlap while creating the first WeakReference to it.
|
||||
}
|
||||
|
||||
~MyObject()
|
||||
{
|
||||
// This will zero all the references - you need to call this in your destructor.
|
||||
masterReference.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// You need to embed a variable of this type, with the name "masterReference" inside your object. If the
|
||||
// variable is not public, you should make your class a friend of WeakReference<MyObject> so that the
|
||||
// WeakReference class can access it.
|
||||
WeakReference<MyObject>::Master masterReference;
|
||||
friend class WeakReference<MyObject>;
|
||||
};
|
||||
|
||||
// Here's an example of using a pointer..
|
||||
|
||||
MyObject* n = new MyObject();
|
||||
WeakReference<MyObject> myObjectRef = n;
|
||||
|
||||
MyObject* pointer1 = myObjectRef; // returns a valid pointer to 'n'
|
||||
delete n;
|
||||
MyObject* pointer2 = myObjectRef; // returns a null pointer
|
||||
@endcode
|
||||
|
||||
@see WeakReference::Master
|
||||
*/
|
||||
template <class ObjectType, class ReferenceCountingType = SharedObject>
|
||||
class WeakReference
|
||||
{
|
||||
public:
|
||||
/** Creates a null SafePointer. */
|
||||
inline WeakReference() noexcept {}
|
||||
|
||||
/** Creates a WeakReference that points at the given object. */
|
||||
WeakReference (ObjectType* const object) : holder (getRef (object)) {}
|
||||
|
||||
/** Creates a copy of another WeakReference. */
|
||||
WeakReference (const WeakReference& other) noexcept : holder (other.holder) {}
|
||||
|
||||
/** Copies another pointer to this one. */
|
||||
WeakReference& operator= (const WeakReference& other) { holder = other.holder; return *this; }
|
||||
|
||||
/** Copies another pointer to this one. */
|
||||
WeakReference& operator= (ObjectType* const newObject) { holder = getRef (newObject); return *this; }
|
||||
|
||||
#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
WeakReference (WeakReference&& other) noexcept : holder (static_cast <SharedRef&&> (other.holder)) {}
|
||||
WeakReference& operator= (WeakReference&& other) noexcept { holder = static_cast <SharedRef&&> (other.holder); return *this; }
|
||||
#endif
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
ObjectType* get() const noexcept { return holder != nullptr ? holder->get() : nullptr; }
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
operator ObjectType*() const noexcept { return get(); }
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
ObjectType* operator->() noexcept { return get(); }
|
||||
|
||||
/** Returns the object that this pointer refers to, or null if the object no longer exists. */
|
||||
const ObjectType* operator->() const noexcept { return get(); }
|
||||
|
||||
/** This returns true if this reference has been pointing at an object, but that object has
|
||||
since been deleted.
|
||||
|
||||
If this reference was only ever pointing at a null pointer, this will return false. Using
|
||||
operator=() to make this refer to a different object will reset this flag to match the status
|
||||
of the reference from which you're copying.
|
||||
*/
|
||||
bool wasObjectDeleted() const noexcept { return holder != nullptr && holder->get() == nullptr; }
|
||||
|
||||
bool operator== (ObjectType* const object) const noexcept { return get() == object; }
|
||||
bool operator!= (ObjectType* const object) const noexcept { return get() != object; }
|
||||
|
||||
//==============================================================================
|
||||
/** This class is used internally by the WeakReference class - don't use it directly
|
||||
in your code!
|
||||
@see WeakReference
|
||||
*/
|
||||
class SharedPointer
|
||||
: public ReferenceCountingType
|
||||
, public Uncopyable
|
||||
{
|
||||
public:
|
||||
explicit SharedPointer (ObjectType* const obj) noexcept : owner (obj) {}
|
||||
|
||||
inline ObjectType* get() const noexcept { return owner; }
|
||||
void clearPointer() noexcept { owner = nullptr; }
|
||||
|
||||
private:
|
||||
ObjectType* volatile owner;
|
||||
};
|
||||
|
||||
typedef SharedObjectPtr<SharedPointer> SharedRef;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class is embedded inside an object to which you want to attach WeakReference pointers.
|
||||
See the WeakReference class notes for an example of how to use this class.
|
||||
@see WeakReference
|
||||
*/
|
||||
class Master : public Uncopyable
|
||||
{
|
||||
public:
|
||||
Master() noexcept {}
|
||||
|
||||
~Master()
|
||||
{
|
||||
// You must remember to call clear() in your source object's destructor! See the notes
|
||||
// for the WeakReference class for an example of how to do this.
|
||||
bassert (sharedPointer == nullptr || sharedPointer->get() == nullptr);
|
||||
}
|
||||
|
||||
/** The first call to this method will create an internal object that is shared by all weak
|
||||
references to the object.
|
||||
*/
|
||||
SharedPointer* getSharedPointer (ObjectType* const object)
|
||||
{
|
||||
if (sharedPointer == nullptr)
|
||||
{
|
||||
sharedPointer = new SharedPointer (object);
|
||||
}
|
||||
else
|
||||
{
|
||||
// You're trying to create a weak reference to an object that has already been deleted!!
|
||||
bassert (sharedPointer->get() != nullptr);
|
||||
}
|
||||
|
||||
return sharedPointer;
|
||||
}
|
||||
|
||||
/** The object that owns this master pointer should call this before it gets destroyed,
|
||||
to zero all the references to this object that may be out there. See the WeakReference
|
||||
class notes for an example of how to do this.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
if (sharedPointer != nullptr)
|
||||
sharedPointer->clearPointer();
|
||||
}
|
||||
|
||||
private:
|
||||
SharedRef sharedPointer;
|
||||
};
|
||||
|
||||
private:
|
||||
SharedRef holder;
|
||||
|
||||
static inline SharedPointer* getRef (ObjectType* const o)
|
||||
{
|
||||
return (o != nullptr) ? o->masterReference.getSharedPointer (o) : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_WEAKREFERENCE_BEASTHEADER
|
||||
@@ -1,170 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
METHOD (toString, "toString", "()Ljava/lang/String;") \
|
||||
|
||||
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (release, "release", "()V") \
|
||||
METHOD (read, "read", "([BI)I") \
|
||||
METHOD (getPosition, "getPosition", "()J") \
|
||||
METHOD (getTotalLength, "getTotalLength", "()J") \
|
||||
METHOD (isExhausted, "isExhausted", "()Z") \
|
||||
METHOD (setPosition, "setPosition", "(J)Z") \
|
||||
|
||||
DECLARE_JNI_CLASS (HTTPStream, BEAST_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
bool Process::openEmailWithAttachments (const String& targetEmailAddress,
|
||||
const String& emailSubject,
|
||||
const String& bodyText,
|
||||
const StringArray& filesToAttach)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream
|
||||
: public InputStream
|
||||
, LeakChecked <WebInputStream>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
WebInputStream (String address, bool isPost, const MemoryBlock& postData,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers, int timeOutMs, StringPairArray* responseHeaders)
|
||||
{
|
||||
if (! address.contains ("://"))
|
||||
address = "http://" + address;
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
jbyteArray postDataArray = 0;
|
||||
|
||||
if (postData.getSize() > 0)
|
||||
{
|
||||
postDataArray = env->NewByteArray (postData.getSize());
|
||||
env->SetByteArrayRegion (postDataArray, 0, postData.getSize(), (const jbyte*) postData.getData());
|
||||
}
|
||||
|
||||
LocalRef<jobject> responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor));
|
||||
|
||||
stream = GlobalRef (env->CallStaticObjectMethod (BeastAppActivity,
|
||||
BeastAppActivity.createHTTPStream,
|
||||
javaString (address).get(),
|
||||
(jboolean) isPost,
|
||||
postDataArray,
|
||||
javaString (headers).get(),
|
||||
(jint) timeOutMs,
|
||||
responseHeaderBuffer.get()));
|
||||
|
||||
if (postDataArray != 0)
|
||||
env->DeleteLocalRef (postDataArray);
|
||||
|
||||
if (stream != 0)
|
||||
{
|
||||
StringArray headerLines;
|
||||
|
||||
{
|
||||
LocalRef<jstring> headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(),
|
||||
StringBuffer.toString));
|
||||
headerLines.addLines (beastString (env, headersString));
|
||||
}
|
||||
|
||||
if (responseHeaders != 0)
|
||||
{
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
const String& header = headerLines[i];
|
||||
const String key (header.upToFirstOccurrenceOf (": ", false, false));
|
||||
const String value (header.fromFirstOccurrenceOf (": ", false, false));
|
||||
const String previousValue ((*responseHeaders) [key]);
|
||||
|
||||
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WebInputStream()
|
||||
{
|
||||
if (stream != 0)
|
||||
stream.callVoidMethod (HTTPStream.release);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isExhausted() { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); }
|
||||
int64 getTotalLength() { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; }
|
||||
int64 getPosition() { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; }
|
||||
bool setPosition (int64 wantedPos) { return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); }
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
bassert (buffer != nullptr && bytesToRead >= 0);
|
||||
|
||||
if (stream == nullptr)
|
||||
return 0;
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
jbyteArray javaArray = env->NewByteArray (bytesToRead);
|
||||
|
||||
int numBytes = stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead);
|
||||
|
||||
if (numBytes > 0)
|
||||
env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast <jbyte*> (buffer));
|
||||
|
||||
env->DeleteLocalRef (javaArray);
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
GlobalRef stream;
|
||||
};
|
||||
|
||||
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
||||
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
|
||||
{
|
||||
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders));
|
||||
|
||||
return wi->stream != 0 ? wi.release() : nullptr;
|
||||
}
|
||||
@@ -1,455 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
ifaddrs* addrs = nullptr;
|
||||
|
||||
if (getifaddrs (&addrs) == 0)
|
||||
{
|
||||
for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next)
|
||||
{
|
||||
sockaddr_storage* sto = (sockaddr_storage*) cursor->ifa_addr;
|
||||
if (sto->ss_family == AF_LINK)
|
||||
{
|
||||
const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr;
|
||||
|
||||
#ifndef IFT_ETHER
|
||||
#define IFT_ETHER 6
|
||||
#endif
|
||||
|
||||
if (sadd->sdl_type == IFT_ETHER)
|
||||
result.addIfNotAlreadyThere (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen));
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs (addrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Process::openEmailWithAttachments (const String& /* targetEmailAddress */,
|
||||
const String& /* emailSubject */,
|
||||
const String& /* bodyText */,
|
||||
const StringArray& /* filesToAttach */)
|
||||
{
|
||||
bassertfalse; // xxx todo
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream
|
||||
: public InputStream
|
||||
, LeakChecked <WebInputStream>
|
||||
{
|
||||
public:
|
||||
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
|
||||
: socketHandle (-1), levelsOfRedirection (0),
|
||||
address (address_), headers (headers_), postData (postData_), position (0),
|
||||
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
|
||||
{
|
||||
createConnection (progressCallback, progressCallbackContext);
|
||||
|
||||
if (responseHeaders != nullptr && ! isError())
|
||||
{
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
const String& headersEntry = headerLines[i];
|
||||
const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false));
|
||||
const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false));
|
||||
const String previousValue ((*responseHeaders) [key]);
|
||||
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WebInputStream()
|
||||
{
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isError() const { return socketHandle < 0; }
|
||||
bool isExhausted() { return finished; }
|
||||
int64 getPosition() { return position; }
|
||||
|
||||
int64 getTotalLength()
|
||||
{
|
||||
//xxx to do
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
if (finished || isError())
|
||||
return 0;
|
||||
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
FD_SET (socketHandle, &readbits);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = bmax (1, timeOutMs / 1000);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return 0; // (timeout)
|
||||
|
||||
const int bytesRead = bmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
|
||||
if (bytesRead == 0)
|
||||
finished = true;
|
||||
position += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (isError())
|
||||
return false;
|
||||
|
||||
if (wantedPos != position)
|
||||
{
|
||||
finished = false;
|
||||
|
||||
if (wantedPos < position)
|
||||
{
|
||||
closeSocket();
|
||||
position = 0;
|
||||
createConnection (0, 0);
|
||||
}
|
||||
|
||||
skipNextBytes (wantedPos - position);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
int socketHandle, levelsOfRedirection;
|
||||
StringArray headerLines;
|
||||
String address, headers;
|
||||
MemoryBlock postData;
|
||||
int64 position;
|
||||
bool finished;
|
||||
const bool isPost;
|
||||
const int timeOutMs;
|
||||
|
||||
void closeSocket()
|
||||
{
|
||||
if (socketHandle >= 0)
|
||||
close (socketHandle);
|
||||
|
||||
socketHandle = -1;
|
||||
levelsOfRedirection = 0;
|
||||
}
|
||||
|
||||
void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext)
|
||||
{
|
||||
closeSocket();
|
||||
|
||||
uint32 timeOutTime = Time::getMillisecondCounter();
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutTime += 60000;
|
||||
else if (timeOutMs < 0)
|
||||
timeOutTime = 0xffffffff;
|
||||
else
|
||||
timeOutTime += timeOutMs;
|
||||
|
||||
String hostName, hostPath;
|
||||
int hostPort;
|
||||
if (! decomposeURL (address, hostName, hostPath, hostPort))
|
||||
return;
|
||||
|
||||
String serverName, proxyName, proxyPath;
|
||||
int proxyPort = 0;
|
||||
int port = 0;
|
||||
|
||||
const String proxyURL (getenv ("http_proxy"));
|
||||
if (proxyURL.startsWithIgnoreCase ("http://"))
|
||||
{
|
||||
if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
|
||||
return;
|
||||
|
||||
serverName = proxyName;
|
||||
port = proxyPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverName = hostName;
|
||||
port = hostPort;
|
||||
}
|
||||
|
||||
struct addrinfo hints;
|
||||
zerostruct (hints);
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
|
||||
struct addrinfo* result = nullptr;
|
||||
if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0)
|
||||
return;
|
||||
|
||||
socketHandle = socket (result->ai_family, result->ai_socktype, 0);
|
||||
|
||||
if (socketHandle == -1)
|
||||
{
|
||||
freeaddrinfo (result);
|
||||
return;
|
||||
}
|
||||
|
||||
int receiveBufferSize = 16384;
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize));
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
|
||||
|
||||
#if BEAST_MAC
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
|
||||
#endif
|
||||
|
||||
if (connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1)
|
||||
{
|
||||
closeSocket();
|
||||
freeaddrinfo (result);
|
||||
return;
|
||||
}
|
||||
|
||||
freeaddrinfo (result);
|
||||
|
||||
{
|
||||
const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort,
|
||||
hostPath, address, headers, postData, isPost));
|
||||
|
||||
if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext))
|
||||
{
|
||||
closeSocket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String responseHeader (readResponse (socketHandle, timeOutTime));
|
||||
|
||||
if (responseHeader.isNotEmpty())
|
||||
{
|
||||
headerLines = StringArray::fromLines (responseHeader);
|
||||
|
||||
const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false)
|
||||
.substring (0, 3).getIntValue();
|
||||
|
||||
//int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue();
|
||||
//bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked");
|
||||
|
||||
String location (findHeaderItem (headerLines, "Location:"));
|
||||
|
||||
if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty())
|
||||
{
|
||||
if (! location.startsWithIgnoreCase ("http://"))
|
||||
location = "http://" + location;
|
||||
|
||||
if (++levelsOfRedirection <= 3)
|
||||
{
|
||||
address = location;
|
||||
createConnection (progressCallback, progressCallbackContext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
levelsOfRedirection = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String readResponse (const int socketHandle, const uint32 timeOutTime)
|
||||
{
|
||||
int bytesRead = 0, numConsecutiveLFs = 0;
|
||||
MemoryBlock buffer (1024, true);
|
||||
|
||||
while (numConsecutiveLFs < 2 && bytesRead < 32768
|
||||
&& Time::getMillisecondCounter() <= timeOutTime)
|
||||
{
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
FD_SET (socketHandle, &readbits);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = bmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return String::empty; // (timeout)
|
||||
|
||||
buffer.ensureSize (bytesRead + 8, true);
|
||||
char* const dest = (char*) buffer.getData() + bytesRead;
|
||||
|
||||
if (recv (socketHandle, dest, 1, 0) == -1)
|
||||
return String::empty;
|
||||
|
||||
const char lastByte = *dest;
|
||||
++bytesRead;
|
||||
|
||||
if (lastByte == '\n')
|
||||
++numConsecutiveLFs;
|
||||
else if (lastByte != '\r')
|
||||
numConsecutiveLFs = 0;
|
||||
}
|
||||
|
||||
const String header (CharPointer_UTF8 ((const char*) buffer.getData()));
|
||||
|
||||
if (header.startsWithIgnoreCase ("HTTP/"))
|
||||
return header.trimEnd();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value)
|
||||
{
|
||||
if (! headers.containsIgnoreCase (key))
|
||||
dest << "\r\n" << key << ' ' << value;
|
||||
}
|
||||
|
||||
static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port)
|
||||
{
|
||||
dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host;
|
||||
|
||||
if (port > 0)
|
||||
dest << ':' << port;
|
||||
}
|
||||
|
||||
static MemoryBlock createRequestHeader (const String& hostName, const int hostPort,
|
||||
const String& proxyName, const int proxyPort,
|
||||
const String& hostPath, const String& originalURL,
|
||||
const String& userHeaders, const MemoryBlock& postData,
|
||||
const bool isPost)
|
||||
{
|
||||
MemoryOutputStream header;
|
||||
|
||||
if (proxyName.isEmpty())
|
||||
writeHost (header, isPost, hostPath, hostName, hostPort);
|
||||
else
|
||||
writeHost (header, isPost, originalURL, proxyName, proxyPort);
|
||||
|
||||
writeValueIfNotPresent (header, userHeaders, "User-Agent:", "BEAST/" BEAST_STRINGIFY(BEAST_MAJOR_VERSION)
|
||||
"." BEAST_STRINGIFY(BEAST_MINOR_VERSION)
|
||||
"." BEAST_STRINGIFY(BEAST_BUILDNUMBER));
|
||||
writeValueIfNotPresent (header, userHeaders, "Connection:", "Close");
|
||||
|
||||
if (isPost)
|
||||
writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize()));
|
||||
|
||||
header << "\r\n" << userHeaders
|
||||
<< "\r\n" << postData;
|
||||
|
||||
return header.getMemoryBlock();
|
||||
}
|
||||
|
||||
static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext)
|
||||
{
|
||||
size_t totalHeaderSent = 0;
|
||||
|
||||
while (totalHeaderSent < requestHeader.getSize())
|
||||
{
|
||||
if (Time::getMillisecondCounter() > timeOutTime)
|
||||
return false;
|
||||
|
||||
const int numToSend = bmin (1024, (int) (requestHeader.getSize() - totalHeaderSent));
|
||||
|
||||
if (send (socketHandle, static_cast <const char*> (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend)
|
||||
return false;
|
||||
|
||||
totalHeaderSent += numToSend;
|
||||
|
||||
if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decomposeURL (const String& url, String& host, String& path, int& port)
|
||||
{
|
||||
if (! url.startsWithIgnoreCase ("http://"))
|
||||
return false;
|
||||
|
||||
const int nextSlash = url.indexOfChar (7, '/');
|
||||
int nextColon = url.indexOfChar (7, ':');
|
||||
if (nextColon > nextSlash && nextSlash > 0)
|
||||
nextColon = -1;
|
||||
|
||||
if (nextColon >= 0)
|
||||
{
|
||||
host = url.substring (7, nextColon);
|
||||
|
||||
if (nextSlash >= 0)
|
||||
port = url.substring (nextColon + 1, nextSlash).getIntValue();
|
||||
else
|
||||
port = url.substring (nextColon + 1).getIntValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 80;
|
||||
|
||||
if (nextSlash >= 0)
|
||||
host = url.substring (7, nextSlash);
|
||||
else
|
||||
host = url.substring (7);
|
||||
}
|
||||
|
||||
if (nextSlash >= 0)
|
||||
path = url.substring (nextSlash);
|
||||
else
|
||||
path = "/";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static String findHeaderItem (const StringArray& lines, const String& itemName)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
if (lines[i].startsWithIgnoreCase (itemName))
|
||||
return lines[i].substring (itemName.length()).trim();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
};
|
||||
|
||||
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
||||
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
|
||||
{
|
||||
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders));
|
||||
|
||||
return wi->isError() ? nullptr : wi.release();
|
||||
}
|
||||
@@ -1,457 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
const int s = socket (AF_INET, SOCK_DGRAM, 0);
|
||||
if (s != -1)
|
||||
{
|
||||
char buf [1024];
|
||||
struct ifconf ifc;
|
||||
ifc.ifc_len = sizeof (buf);
|
||||
ifc.ifc_buf = buf;
|
||||
ioctl (s, SIOCGIFCONF, &ifc);
|
||||
|
||||
for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name);
|
||||
|
||||
if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0
|
||||
&& (ifr.ifr_flags & IFF_LOOPBACK) == 0
|
||||
&& ioctl (s, SIOCGIFHWADDR, &ifr) == 0)
|
||||
{
|
||||
result.addIfNotAlreadyThere (MACAddress ((const uint8*) ifr.ifr_hwaddr.sa_data));
|
||||
}
|
||||
}
|
||||
|
||||
close (s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Process::openEmailWithAttachments (const String& /* targetEmailAddress */,
|
||||
const String& /* emailSubject */,
|
||||
const String& /* bodyText */,
|
||||
const StringArray& /* filesToAttach */)
|
||||
{
|
||||
bassertfalse; // xxx todo
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream
|
||||
: public InputStream
|
||||
, LeakChecked <WebInputStream>
|
||||
{
|
||||
public:
|
||||
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
|
||||
: socketHandle (-1), levelsOfRedirection (0),
|
||||
address (address_), headers (headers_), postData (postData_), position (0),
|
||||
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
|
||||
{
|
||||
createConnection (progressCallback, progressCallbackContext);
|
||||
|
||||
if (responseHeaders != nullptr && ! isError())
|
||||
{
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
const String& headersEntry = headerLines[i];
|
||||
const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false));
|
||||
const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false));
|
||||
const String previousValue ((*responseHeaders) [key]);
|
||||
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WebInputStream()
|
||||
{
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isError() const { return socketHandle < 0; }
|
||||
bool isExhausted() { return finished; }
|
||||
int64 getPosition() { return position; }
|
||||
|
||||
int64 getTotalLength()
|
||||
{
|
||||
//xxx to do
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
if (finished || isError())
|
||||
return 0;
|
||||
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
FD_SET (socketHandle, &readbits);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = bmax (1, timeOutMs / 1000);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return 0; // (timeout)
|
||||
|
||||
const int bytesRead = bmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL));
|
||||
if (bytesRead == 0)
|
||||
finished = true;
|
||||
position += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (isError())
|
||||
return false;
|
||||
|
||||
if (wantedPos != position)
|
||||
{
|
||||
finished = false;
|
||||
|
||||
if (wantedPos < position)
|
||||
{
|
||||
closeSocket();
|
||||
position = 0;
|
||||
createConnection (0, 0);
|
||||
}
|
||||
|
||||
skipNextBytes (wantedPos - position);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
int socketHandle, levelsOfRedirection;
|
||||
StringArray headerLines;
|
||||
String address, headers;
|
||||
MemoryBlock postData;
|
||||
int64 position;
|
||||
bool finished;
|
||||
const bool isPost;
|
||||
const int timeOutMs;
|
||||
|
||||
void closeSocket()
|
||||
{
|
||||
if (socketHandle >= 0)
|
||||
close (socketHandle);
|
||||
|
||||
socketHandle = -1;
|
||||
levelsOfRedirection = 0;
|
||||
}
|
||||
|
||||
void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext)
|
||||
{
|
||||
closeSocket();
|
||||
|
||||
uint32 timeOutTime = Time::getMillisecondCounter();
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutTime += 60000;
|
||||
else if (timeOutMs < 0)
|
||||
timeOutTime = 0xffffffff;
|
||||
else
|
||||
timeOutTime += timeOutMs;
|
||||
|
||||
String hostName, hostPath;
|
||||
int hostPort;
|
||||
if (! decomposeURL (address, hostName, hostPath, hostPort))
|
||||
return;
|
||||
|
||||
String serverName, proxyName, proxyPath;
|
||||
int proxyPort = 0;
|
||||
int port = 0;
|
||||
|
||||
const String proxyURL (getenv ("http_proxy"));
|
||||
if (proxyURL.startsWithIgnoreCase ("http://"))
|
||||
{
|
||||
if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
|
||||
return;
|
||||
|
||||
serverName = proxyName;
|
||||
port = proxyPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverName = hostName;
|
||||
port = hostPort;
|
||||
}
|
||||
|
||||
struct addrinfo hints;
|
||||
zerostruct (hints);
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
|
||||
struct addrinfo* result = nullptr;
|
||||
if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0)
|
||||
return;
|
||||
|
||||
socketHandle = socket (result->ai_family, result->ai_socktype, 0);
|
||||
|
||||
if (socketHandle == -1)
|
||||
{
|
||||
freeaddrinfo (result);
|
||||
return;
|
||||
}
|
||||
|
||||
int receiveBufferSize = 16384;
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize));
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
|
||||
|
||||
#if BEAST_MAC
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
|
||||
#endif
|
||||
|
||||
if (connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1)
|
||||
{
|
||||
closeSocket();
|
||||
freeaddrinfo (result);
|
||||
return;
|
||||
}
|
||||
|
||||
freeaddrinfo (result);
|
||||
|
||||
{
|
||||
const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort,
|
||||
hostPath, address, headers, postData, isPost));
|
||||
|
||||
if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext))
|
||||
{
|
||||
closeSocket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String responseHeader (readResponse (socketHandle, timeOutTime));
|
||||
|
||||
if (responseHeader.isNotEmpty())
|
||||
{
|
||||
headerLines = StringArray::fromLines (responseHeader);
|
||||
|
||||
const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false)
|
||||
.substring (0, 3).getIntValue();
|
||||
|
||||
//int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue();
|
||||
//bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked");
|
||||
|
||||
String location (findHeaderItem (headerLines, "Location:"));
|
||||
|
||||
if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty())
|
||||
{
|
||||
if (! location.startsWithIgnoreCase ("http://"))
|
||||
location = "http://" + location;
|
||||
|
||||
if (++levelsOfRedirection <= 3)
|
||||
{
|
||||
address = location;
|
||||
createConnection (progressCallback, progressCallbackContext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
levelsOfRedirection = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String readResponse (const int socketHandle, const uint32 timeOutTime)
|
||||
{
|
||||
int bytesRead = 0, numConsecutiveLFs = 0;
|
||||
MemoryBlock buffer (1024, true);
|
||||
|
||||
while (numConsecutiveLFs < 2 && bytesRead < 32768
|
||||
&& Time::getMillisecondCounter() <= timeOutTime)
|
||||
{
|
||||
fd_set readbits;
|
||||
FD_ZERO (&readbits);
|
||||
FD_SET (socketHandle, &readbits);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = bmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
|
||||
return String::empty; // (timeout)
|
||||
|
||||
buffer.ensureSize (bytesRead + 8, true);
|
||||
char* const dest = (char*) buffer.getData() + bytesRead;
|
||||
|
||||
if (recv (socketHandle, dest, 1, 0) == -1)
|
||||
return String::empty;
|
||||
|
||||
const char lastByte = *dest;
|
||||
++bytesRead;
|
||||
|
||||
if (lastByte == '\n')
|
||||
++numConsecutiveLFs;
|
||||
else if (lastByte != '\r')
|
||||
numConsecutiveLFs = 0;
|
||||
}
|
||||
|
||||
const String header (CharPointer_UTF8 ((const char*) buffer.getData()));
|
||||
|
||||
if (header.startsWithIgnoreCase ("HTTP/"))
|
||||
return header.trimEnd();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value)
|
||||
{
|
||||
if (! headers.containsIgnoreCase (key))
|
||||
dest << "\r\n" << key << ' ' << value;
|
||||
}
|
||||
|
||||
static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port)
|
||||
{
|
||||
dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host;
|
||||
|
||||
if (port > 0)
|
||||
dest << ':' << port;
|
||||
}
|
||||
|
||||
static MemoryBlock createRequestHeader (const String& hostName, const int hostPort,
|
||||
const String& proxyName, const int proxyPort,
|
||||
const String& hostPath, const String& originalURL,
|
||||
const String& userHeaders, const MemoryBlock& postData,
|
||||
const bool isPost)
|
||||
{
|
||||
MemoryOutputStream header;
|
||||
|
||||
if (proxyName.isEmpty())
|
||||
writeHost (header, isPost, hostPath, hostName, hostPort);
|
||||
else
|
||||
writeHost (header, isPost, originalURL, proxyName, proxyPort);
|
||||
|
||||
writeValueIfNotPresent (header, userHeaders, "User-Agent:", "BEAST/" BEAST_STRINGIFY(BEAST_MAJOR_VERSION)
|
||||
"." BEAST_STRINGIFY(BEAST_MINOR_VERSION)
|
||||
"." BEAST_STRINGIFY(BEAST_BUILDNUMBER));
|
||||
writeValueIfNotPresent (header, userHeaders, "Connection:", "Close");
|
||||
|
||||
if (isPost)
|
||||
writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize()));
|
||||
|
||||
header << "\r\n" << userHeaders
|
||||
<< "\r\n" << postData;
|
||||
|
||||
return header.getMemoryBlock();
|
||||
}
|
||||
|
||||
static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext)
|
||||
{
|
||||
size_t totalHeaderSent = 0;
|
||||
|
||||
while (totalHeaderSent < requestHeader.getSize())
|
||||
{
|
||||
if (Time::getMillisecondCounter() > timeOutTime)
|
||||
return false;
|
||||
|
||||
const int numToSend = bmin (1024, (int) (requestHeader.getSize() - totalHeaderSent));
|
||||
|
||||
if (send (socketHandle, static_cast <const char*> (requestHeader.getData()) + totalHeaderSent, numToSend, 0) != numToSend)
|
||||
return false;
|
||||
|
||||
totalHeaderSent += numToSend;
|
||||
|
||||
if (progressCallback != nullptr && ! progressCallback (progressCallbackContext, totalHeaderSent, requestHeader.getSize()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decomposeURL (const String& url, String& host, String& path, int& port)
|
||||
{
|
||||
if (! url.startsWithIgnoreCase ("http://"))
|
||||
return false;
|
||||
|
||||
const int nextSlash = url.indexOfChar (7, '/');
|
||||
int nextColon = url.indexOfChar (7, ':');
|
||||
if (nextColon > nextSlash && nextSlash > 0)
|
||||
nextColon = -1;
|
||||
|
||||
if (nextColon >= 0)
|
||||
{
|
||||
host = url.substring (7, nextColon);
|
||||
|
||||
if (nextSlash >= 0)
|
||||
port = url.substring (nextColon + 1, nextSlash).getIntValue();
|
||||
else
|
||||
port = url.substring (nextColon + 1).getIntValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 80;
|
||||
|
||||
if (nextSlash >= 0)
|
||||
host = url.substring (7, nextSlash);
|
||||
else
|
||||
host = url.substring (7);
|
||||
}
|
||||
|
||||
if (nextSlash >= 0)
|
||||
path = url.substring (nextSlash);
|
||||
else
|
||||
path = "/";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static String findHeaderItem (const StringArray& lines, const String& itemName)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
if (lines[i].startsWithIgnoreCase (itemName))
|
||||
return lines[i].substring (itemName.length()).trim();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
};
|
||||
|
||||
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
||||
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
|
||||
{
|
||||
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders));
|
||||
|
||||
return wi->isError() ? nullptr : wi.release();
|
||||
}
|
||||
@@ -1,428 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
ifaddrs* addrs = nullptr;
|
||||
|
||||
if (getifaddrs (&addrs) == 0)
|
||||
{
|
||||
for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next)
|
||||
{
|
||||
sockaddr_storage* sto = (sockaddr_storage*) cursor->ifa_addr;
|
||||
if (sto->ss_family == AF_LINK)
|
||||
{
|
||||
const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr;
|
||||
|
||||
#ifndef IFT_ETHER
|
||||
#define IFT_ETHER 6
|
||||
#endif
|
||||
|
||||
if (sadd->sdl_type == IFT_ETHER)
|
||||
result.addIfNotAlreadyThere (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen));
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs (addrs);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Process::openEmailWithAttachments (const String& targetEmailAddress,
|
||||
const String& emailSubject,
|
||||
const String& bodyText,
|
||||
const StringArray& filesToAttach)
|
||||
{
|
||||
#if BEAST_IOS
|
||||
//xxx probably need to use MFMailComposeViewController
|
||||
bassertfalse;
|
||||
return false;
|
||||
#else
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
String script;
|
||||
script << "tell application \"Mail\"\r\n"
|
||||
"set newMessage to make new outgoing message with properties {subject:\""
|
||||
<< emailSubject.replace ("\"", "\\\"")
|
||||
<< "\", content:\""
|
||||
<< bodyText.replace ("\"", "\\\"")
|
||||
<< "\" & return & return}\r\n"
|
||||
"tell newMessage\r\n"
|
||||
"set visible to true\r\n"
|
||||
"set sender to \"sdfsdfsdfewf\"\r\n"
|
||||
"make new to recipient at end of to recipients with properties {address:\""
|
||||
<< targetEmailAddress
|
||||
<< "\"}\r\n";
|
||||
|
||||
for (int i = 0; i < filesToAttach.size(); ++i)
|
||||
{
|
||||
script << "tell content\r\n"
|
||||
"make new attachment with properties {file name:\""
|
||||
<< filesToAttach[i].replace ("\"", "\\\"")
|
||||
<< "\"} at after the last paragraph\r\n"
|
||||
"end tell\r\n";
|
||||
}
|
||||
|
||||
script << "end tell\r\n"
|
||||
"end tell\r\n";
|
||||
|
||||
NSAppleScript* s = [[NSAppleScript alloc] initWithSource: beastStringToNS (script)];
|
||||
NSDictionary* error = nil;
|
||||
const bool ok = [s executeAndReturnError: &error] != nil;
|
||||
[s release];
|
||||
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class URLConnectionState
|
||||
: public Thread
|
||||
, LeakChecked <URLConnectionState>
|
||||
, public Uncopyable
|
||||
{
|
||||
public:
|
||||
URLConnectionState (NSURLRequest* req)
|
||||
: Thread ("http connection"),
|
||||
contentLength (-1),
|
||||
delegate (nil),
|
||||
request ([req retain]),
|
||||
connection (nil),
|
||||
data ([[NSMutableData data] retain]),
|
||||
headers (nil),
|
||||
initialised (false),
|
||||
hasFailed (false),
|
||||
hasFinished (false)
|
||||
{
|
||||
static DelegateClass cls;
|
||||
delegate = [cls.createInstance() init];
|
||||
DelegateClass::setState (delegate, this);
|
||||
}
|
||||
|
||||
~URLConnectionState()
|
||||
{
|
||||
stop();
|
||||
[connection release];
|
||||
[data release];
|
||||
[request release];
|
||||
[headers release];
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
bool start (URL::OpenStreamProgressCallback* callback, void* context)
|
||||
{
|
||||
startThread();
|
||||
|
||||
while (isThreadRunning() && ! initialised)
|
||||
{
|
||||
if (callback != nullptr)
|
||||
callback (context, -1, (int) [[request HTTPBody] length]);
|
||||
|
||||
Thread::sleep (1);
|
||||
}
|
||||
|
||||
return connection != nil && ! hasFailed;
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
[connection cancel];
|
||||
stopThread (10000);
|
||||
}
|
||||
|
||||
int read (char* dest, int numBytes)
|
||||
{
|
||||
int numDone = 0;
|
||||
|
||||
while (numBytes > 0)
|
||||
{
|
||||
const int available = bmin (numBytes, (int) [data length]);
|
||||
|
||||
if (available > 0)
|
||||
{
|
||||
const ScopedLock sl (dataLock);
|
||||
[data getBytes: dest length: (NSUInteger) available];
|
||||
[data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0];
|
||||
|
||||
numDone += available;
|
||||
numBytes -= available;
|
||||
dest += available;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasFailed || hasFinished)
|
||||
break;
|
||||
|
||||
Thread::sleep (1);
|
||||
}
|
||||
}
|
||||
|
||||
return numDone;
|
||||
}
|
||||
|
||||
void didReceiveResponse (NSURLResponse* response)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (dataLock);
|
||||
[data setLength: 0];
|
||||
}
|
||||
|
||||
initialised = true;
|
||||
contentLength = [response expectedContentLength];
|
||||
|
||||
[headers release];
|
||||
headers = nil;
|
||||
|
||||
if ([response isKindOfClass: [NSHTTPURLResponse class]])
|
||||
headers = [[((NSHTTPURLResponse*) response) allHeaderFields] retain];
|
||||
}
|
||||
|
||||
void didFailWithError (NSError* error)
|
||||
{
|
||||
DBG (nsStringToBeast ([error description])); (void) error;
|
||||
hasFailed = true;
|
||||
initialised = true;
|
||||
signalThreadShouldExit();
|
||||
}
|
||||
|
||||
void didReceiveData (NSData* newData)
|
||||
{
|
||||
const ScopedLock sl (dataLock);
|
||||
[data appendData: newData];
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
void didSendBodyData (int /*totalBytesWritten*/, int /*totalBytesExpected*/)
|
||||
{
|
||||
}
|
||||
|
||||
void finishedLoading()
|
||||
{
|
||||
hasFinished = true;
|
||||
initialised = true;
|
||||
signalThreadShouldExit();
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
connection = [[NSURLConnection alloc] initWithRequest: request
|
||||
delegate: delegate];
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64 contentLength;
|
||||
CriticalSection dataLock;
|
||||
NSObject* delegate;
|
||||
NSURLRequest* request;
|
||||
NSURLConnection* connection;
|
||||
NSMutableData* data;
|
||||
NSDictionary* headers;
|
||||
bool initialised, hasFailed, hasFinished;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct DelegateClass : public ObjCClass <NSObject>
|
||||
{
|
||||
DelegateClass() : ObjCClass <NSObject> ("BEASTAppDelegate_")
|
||||
{
|
||||
addIvar <URLConnectionState*> ("state");
|
||||
|
||||
addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@");
|
||||
addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@");
|
||||
addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@");
|
||||
addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:totalBytesExpectedToWrite:),
|
||||
connectionDidSendBodyData, "v@:@iii");
|
||||
addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static void setState (id self, URLConnectionState* state) { object_setInstanceVariable (self, "state", state); }
|
||||
static URLConnectionState* getState (id self) { return getIvar<URLConnectionState*> (self, "state"); }
|
||||
|
||||
private:
|
||||
static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response)
|
||||
{
|
||||
getState (self)->didReceiveResponse (response);
|
||||
}
|
||||
|
||||
static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error)
|
||||
{
|
||||
getState (self)->didFailWithError (error);
|
||||
}
|
||||
|
||||
static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData)
|
||||
{
|
||||
getState (self)->didReceiveData (newData);
|
||||
}
|
||||
|
||||
static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected)
|
||||
{
|
||||
getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected);
|
||||
}
|
||||
|
||||
static void connectionDidFinishLoading (id self, SEL, NSURLConnection*)
|
||||
{
|
||||
getState (self)->finishedLoading();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream
|
||||
: public InputStream
|
||||
, LeakChecked <WebInputStream>
|
||||
{
|
||||
public:
|
||||
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
|
||||
: address (address_), headers (headers_), postData (postData_), position (0),
|
||||
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
|
||||
{
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
createConnection (progressCallback, progressCallbackContext);
|
||||
|
||||
if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil)
|
||||
{
|
||||
NSEnumerator* enumerator = [connection->headers keyEnumerator];
|
||||
NSString* key;
|
||||
|
||||
while ((key = [enumerator nextObject]) != nil)
|
||||
responseHeaders->set (nsStringToBeast (key),
|
||||
nsStringToBeast ((NSString*) [connection->headers objectForKey: key]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isError() const { return connection == nullptr; }
|
||||
int64 getTotalLength() { return connection == nullptr ? -1 : connection->contentLength; }
|
||||
bool isExhausted() { return finished; }
|
||||
int64 getPosition() { return position; }
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
bassert (buffer != nullptr && bytesToRead >= 0);
|
||||
|
||||
if (finished || isError())
|
||||
return 0;
|
||||
|
||||
BEAST_AUTORELEASEPOOL
|
||||
{
|
||||
const int bytesRead = connection->read (static_cast <char*> (buffer), bytesToRead);
|
||||
position += bytesRead;
|
||||
|
||||
if (bytesRead == 0)
|
||||
finished = true;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (wantedPos != position)
|
||||
{
|
||||
finished = false;
|
||||
|
||||
if (wantedPos < position)
|
||||
{
|
||||
connection = nullptr;
|
||||
position = 0;
|
||||
createConnection (0, 0);
|
||||
}
|
||||
|
||||
skipNextBytes (wantedPos - position);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPointer<URLConnectionState> connection;
|
||||
String address, headers;
|
||||
MemoryBlock postData;
|
||||
int64 position;
|
||||
bool finished;
|
||||
const bool isPost;
|
||||
const int timeOutMs;
|
||||
|
||||
void createConnection (URL::OpenStreamProgressCallback* progressCallback,
|
||||
void* progressCallbackContext)
|
||||
{
|
||||
bassert (connection == nullptr);
|
||||
|
||||
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: beastStringToNS (address)]
|
||||
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)];
|
||||
|
||||
if (req != nil)
|
||||
{
|
||||
[req setHTTPMethod: nsStringLiteral (isPost ? "POST" : "GET")];
|
||||
//[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData];
|
||||
|
||||
StringArray headerLines;
|
||||
headerLines.addLines (headers);
|
||||
headerLines.removeEmptyStrings (true);
|
||||
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim());
|
||||
const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim());
|
||||
|
||||
if (key.isNotEmpty() && value.isNotEmpty())
|
||||
[req addValue: beastStringToNS (value) forHTTPHeaderField: beastStringToNS (key)];
|
||||
}
|
||||
|
||||
if (isPost && postData.getSize() > 0)
|
||||
[req setHTTPBody: [NSData dataWithBytes: postData.getData()
|
||||
length: postData.getSize()]];
|
||||
|
||||
connection = new URLConnectionState (req);
|
||||
|
||||
if (! connection->start (progressCallback, progressCallbackContext))
|
||||
connection = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
||||
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
|
||||
{
|
||||
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders));
|
||||
|
||||
return wi->isError() ? nullptr : wi.release();
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef INTERNET_FLAG_NEED_FILE
|
||||
#define INTERNET_FLAG_NEED_FILE 0x00000010
|
||||
#endif
|
||||
|
||||
#ifndef INTERNET_OPTION_DISABLE_AUTODIAL
|
||||
#define INTERNET_OPTION_DISABLE_AUTODIAL 70
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream
|
||||
: public InputStream
|
||||
, LeakChecked <WebInputStream>
|
||||
{
|
||||
public:
|
||||
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
|
||||
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
|
||||
: connection (0), request (0),
|
||||
address (address_), headers (headers_), postData (postData_), position (0),
|
||||
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
|
||||
{
|
||||
createConnection (progressCallback, progressCallbackContext);
|
||||
|
||||
if (responseHeaders != nullptr && ! isError())
|
||||
{
|
||||
DWORD bufferSizeBytes = 4096;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
HeapBlock<char> buffer ((size_t) bufferSizeBytes);
|
||||
|
||||
if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0))
|
||||
{
|
||||
StringArray headersArray;
|
||||
headersArray.addLines (reinterpret_cast <const WCHAR*> (buffer.getData()));
|
||||
|
||||
for (int i = 0; i < headersArray.size(); ++i)
|
||||
{
|
||||
const String& header = headersArray[i];
|
||||
const String key (header.upToFirstOccurrenceOf (": ", false, false));
|
||||
const String value (header.fromFirstOccurrenceOf (": ", false, false));
|
||||
const String previousValue ((*responseHeaders) [key]);
|
||||
|
||||
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WebInputStream()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isError() const { return request == 0; }
|
||||
bool isExhausted() { return finished; }
|
||||
int64 getPosition() { return position; }
|
||||
|
||||
int64 getTotalLength()
|
||||
{
|
||||
if (! isError())
|
||||
{
|
||||
DWORD index = 0, result = 0, size = sizeof (result);
|
||||
|
||||
if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index))
|
||||
return (int64) result;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
bassert (buffer != nullptr && bytesToRead >= 0);
|
||||
DWORD bytesRead = 0;
|
||||
|
||||
if (! (finished || isError()))
|
||||
{
|
||||
InternetReadFile (request, buffer, (DWORD) bytesToRead, &bytesRead);
|
||||
position += bytesRead;
|
||||
|
||||
if (bytesRead == 0)
|
||||
finished = true;
|
||||
}
|
||||
|
||||
return (int) bytesRead;
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (isError())
|
||||
return false;
|
||||
|
||||
if (wantedPos != position)
|
||||
{
|
||||
finished = false;
|
||||
position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, 0, FILE_BEGIN, 0);
|
||||
|
||||
if (position == wantedPos)
|
||||
return true;
|
||||
|
||||
if (wantedPos < position)
|
||||
{
|
||||
close();
|
||||
position = 0;
|
||||
createConnection (0, 0);
|
||||
}
|
||||
|
||||
skipNextBytes (wantedPos - position);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
HINTERNET connection, request;
|
||||
String address, headers;
|
||||
MemoryBlock postData;
|
||||
int64 position;
|
||||
bool finished;
|
||||
const bool isPost;
|
||||
int timeOutMs;
|
||||
|
||||
void close()
|
||||
{
|
||||
if (request != 0)
|
||||
{
|
||||
InternetCloseHandle (request);
|
||||
request = 0;
|
||||
}
|
||||
|
||||
if (connection != 0)
|
||||
{
|
||||
InternetCloseHandle (connection);
|
||||
connection = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void createConnection (URL::OpenStreamProgressCallback* progressCallback,
|
||||
void* progressCallbackContext)
|
||||
{
|
||||
static HINTERNET sessionHandle = InternetOpen (_T("beast"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);
|
||||
|
||||
close();
|
||||
|
||||
if (sessionHandle != 0)
|
||||
{
|
||||
// break up the url..
|
||||
const int fileNumChars = 65536;
|
||||
const int serverNumChars = 2048;
|
||||
const int usernameNumChars = 1024;
|
||||
const int passwordNumChars = 1024;
|
||||
HeapBlock<TCHAR> file (fileNumChars), server (serverNumChars),
|
||||
username (usernameNumChars), password (passwordNumChars);
|
||||
|
||||
URL_COMPONENTS uc = { 0 };
|
||||
uc.dwStructSize = sizeof (uc);
|
||||
uc.lpszUrlPath = file;
|
||||
uc.dwUrlPathLength = fileNumChars;
|
||||
uc.lpszHostName = server;
|
||||
uc.dwHostNameLength = serverNumChars;
|
||||
uc.lpszUserName = username;
|
||||
uc.dwUserNameLength = usernameNumChars;
|
||||
uc.lpszPassword = password;
|
||||
uc.dwPasswordLength = passwordNumChars;
|
||||
|
||||
if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc))
|
||||
openConnection (uc, sessionHandle, progressCallback, progressCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
void openConnection (URL_COMPONENTS& uc, HINTERNET sessionHandle,
|
||||
URL::OpenStreamProgressCallback* progressCallback,
|
||||
void* progressCallbackContext)
|
||||
{
|
||||
int disable = 1;
|
||||
InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable));
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutMs = 30000;
|
||||
else if (timeOutMs < 0)
|
||||
timeOutMs = -1;
|
||||
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_RECEIVE_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_SEND_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_DATA_SEND_TIMEOUT);
|
||||
|
||||
const bool isFtp = address.startsWithIgnoreCase ("ftp:");
|
||||
|
||||
connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort,
|
||||
uc.lpszUserName, uc.lpszPassword,
|
||||
isFtp ? (DWORD) INTERNET_SERVICE_FTP
|
||||
: (DWORD) INTERNET_SERVICE_HTTP,
|
||||
0, 0);
|
||||
if (connection != 0)
|
||||
{
|
||||
if (isFtp)
|
||||
request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ,
|
||||
FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0);
|
||||
else
|
||||
openHTTPConnection (uc, progressCallback, progressCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
void applyTimeout (HINTERNET sessionHandle, const DWORD option)
|
||||
{
|
||||
InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs));
|
||||
}
|
||||
|
||||
void openHTTPConnection (URL_COMPONENTS& uc, URL::OpenStreamProgressCallback* progressCallback,
|
||||
void* progressCallbackContext)
|
||||
{
|
||||
const TCHAR* mimeTypes[] = { _T("*/*"), nullptr };
|
||||
|
||||
DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES;
|
||||
|
||||
if (address.startsWithIgnoreCase ("https:"))
|
||||
flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 -
|
||||
// IE7 seems to automatically work out when it's https)
|
||||
|
||||
request = HttpOpenRequest (connection, isPost ? _T("POST") : _T("GET"),
|
||||
uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0);
|
||||
|
||||
if (request != 0)
|
||||
{
|
||||
INTERNET_BUFFERS buffers = { 0 };
|
||||
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
|
||||
buffers.lpcszHeader = headers.toWideCharPointer();
|
||||
buffers.dwHeadersLength = (DWORD) headers.length();
|
||||
buffers.dwBufferTotal = (DWORD) postData.getSize();
|
||||
|
||||
if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
|
||||
{
|
||||
int bytesSent = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const int bytesToDo = bmin (1024, (int) postData.getSize() - bytesSent);
|
||||
DWORD bytesDone = 0;
|
||||
|
||||
if (bytesToDo > 0
|
||||
&& ! InternetWriteFile (request,
|
||||
static_cast <const char*> (postData.getData()) + bytesSent,
|
||||
(DWORD) bytesToDo, &bytesDone))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytesToDo == 0 || (int) bytesDone < bytesToDo)
|
||||
{
|
||||
if (HttpEndRequest (request, 0, 0, 0))
|
||||
return;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bytesSent += bytesDone;
|
||||
|
||||
if (progressCallback != nullptr
|
||||
&& ! progressCallback (progressCallbackContext, bytesSent, (int) postData.getSize()))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
||||
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
|
||||
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
|
||||
{
|
||||
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders));
|
||||
|
||||
return wi->isError() ? nullptr : wi.release();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct GetAdaptersInfoHelper
|
||||
{
|
||||
bool callGetAdaptersInfo()
|
||||
{
|
||||
DynamicLibrary dll ("iphlpapi.dll");
|
||||
BEAST_LOAD_WINAPI_FUNCTION (dll, GetAdaptersInfo, getAdaptersInfo, DWORD, (PIP_ADAPTER_INFO, PULONG))
|
||||
|
||||
if (getAdaptersInfo == nullptr)
|
||||
return false;
|
||||
|
||||
adapterInfo.malloc (1);
|
||||
ULONG len = sizeof (IP_ADAPTER_INFO);
|
||||
|
||||
if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW)
|
||||
adapterInfo.malloc (len, 1);
|
||||
|
||||
return getAdaptersInfo (adapterInfo, &len) == NO_ERROR;
|
||||
}
|
||||
|
||||
HeapBlock<IP_ADAPTER_INFO> adapterInfo;
|
||||
};
|
||||
|
||||
namespace MACAddressHelpers
|
||||
{
|
||||
void getViaGetAdaptersInfo (Array<MACAddress>& result)
|
||||
{
|
||||
GetAdaptersInfoHelper gah;
|
||||
|
||||
if (gah.callGetAdaptersInfo())
|
||||
{
|
||||
for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next)
|
||||
if (adapter->AddressLength >= 6)
|
||||
result.addIfNotAlreadyThere (MACAddress (adapter->Address));
|
||||
}
|
||||
}
|
||||
|
||||
void getViaNetBios (Array<MACAddress>& result)
|
||||
{
|
||||
DynamicLibrary dll ("netapi32.dll");
|
||||
BEAST_LOAD_WINAPI_FUNCTION (dll, Netbios, NetbiosCall, UCHAR, (PNCB))
|
||||
|
||||
if (NetbiosCall != 0)
|
||||
{
|
||||
LANA_ENUM enums = { 0 };
|
||||
|
||||
{
|
||||
NCB ncb = { 0 };
|
||||
ncb.ncb_command = NCBENUM;
|
||||
ncb.ncb_buffer = (unsigned char*) &enums;
|
||||
ncb.ncb_length = sizeof (LANA_ENUM);
|
||||
NetbiosCall (&ncb);
|
||||
}
|
||||
|
||||
for (int i = 0; i < enums.length; ++i)
|
||||
{
|
||||
NCB ncb2 = { 0 };
|
||||
ncb2.ncb_command = NCBRESET;
|
||||
ncb2.ncb_lana_num = enums.lana[i];
|
||||
|
||||
if (NetbiosCall (&ncb2) == 0)
|
||||
{
|
||||
NCB ncb = { 0 };
|
||||
memcpy (ncb.ncb_callname, "* ", NCBNAMSZ);
|
||||
ncb.ncb_command = NCBASTAT;
|
||||
ncb.ncb_lana_num = enums.lana[i];
|
||||
|
||||
struct ASTAT
|
||||
{
|
||||
ADAPTER_STATUS adapt;
|
||||
NAME_BUFFER NameBuff [30];
|
||||
};
|
||||
|
||||
ASTAT astat;
|
||||
zerostruct (astat);
|
||||
ncb.ncb_buffer = (unsigned char*) &astat;
|
||||
ncb.ncb_length = sizeof (ASTAT);
|
||||
|
||||
if (NetbiosCall (&ncb) == 0 && astat.adapt.adapter_type == 0xfe)
|
||||
result.addIfNotAlreadyThere (MACAddress (astat.adapt.adapter_address));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
MACAddressHelpers::getViaGetAdaptersInfo (result);
|
||||
MACAddressHelpers::getViaNetBios (result);
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result)
|
||||
{
|
||||
result.addIfNotAlreadyThere (IPAddress::local());
|
||||
|
||||
GetAdaptersInfoHelper gah;
|
||||
|
||||
if (gah.callGetAdaptersInfo())
|
||||
{
|
||||
for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next)
|
||||
{
|
||||
IPAddress ip (adapter->IpAddressList.IpAddress.String);
|
||||
|
||||
if (ip != IPAddress::any())
|
||||
result.addIfNotAlreadyThere (ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Process::openEmailWithAttachments (const String& targetEmailAddress,
|
||||
const String& emailSubject,
|
||||
const String& bodyText,
|
||||
const StringArray& filesToAttach)
|
||||
{
|
||||
DynamicLibrary dll ("MAPI32.dll");
|
||||
BEAST_LOAD_WINAPI_FUNCTION (dll, MAPISendMail, mapiSendMail,
|
||||
ULONG, (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG))
|
||||
|
||||
if (mapiSendMail == nullptr)
|
||||
return false;
|
||||
|
||||
MapiMessage message = { 0 };
|
||||
message.lpszSubject = (LPSTR) emailSubject.toRawUTF8();
|
||||
message.lpszNoteText = (LPSTR) bodyText.toRawUTF8();
|
||||
|
||||
MapiRecipDesc recip = { 0 };
|
||||
recip.ulRecipClass = MAPI_TO;
|
||||
String targetEmailAddress_ (targetEmailAddress);
|
||||
if (targetEmailAddress_.isEmpty())
|
||||
targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address)
|
||||
recip.lpszName = (LPSTR) targetEmailAddress_.toRawUTF8();
|
||||
message.nRecipCount = 1;
|
||||
message.lpRecips = &recip;
|
||||
|
||||
HeapBlock <MapiFileDesc> files;
|
||||
files.calloc ((size_t) filesToAttach.size());
|
||||
|
||||
message.nFileCount = (ULONG) filesToAttach.size();
|
||||
message.lpFiles = files;
|
||||
|
||||
for (int i = 0; i < filesToAttach.size(); ++i)
|
||||
{
|
||||
files[i].nPosition = (ULONG) -1;
|
||||
files[i].lpszPathName = (LPSTR) filesToAttach[i].toRawUTF8();
|
||||
}
|
||||
|
||||
return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS;
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
IPAddress::IPAddress() noexcept
|
||||
{
|
||||
address[0] = 0; address[1] = 0;
|
||||
address[2] = 0; address[3] = 0;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (const uint8 bytes[4]) noexcept
|
||||
{
|
||||
address[0] = bytes[0]; address[1] = bytes[1];
|
||||
address[2] = bytes[2]; address[3] = bytes[3];
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept
|
||||
{
|
||||
address[0] = a0; address[1] = a1;
|
||||
address[2] = a2; address[3] = a3;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (uint32 n) noexcept
|
||||
{
|
||||
address[0] = (n >> 24);
|
||||
address[1] = (n >> 16) & 255;
|
||||
address[2] = (n >> 8) & 255;
|
||||
address[3] = (n & 255);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress (const String& adr)
|
||||
{
|
||||
StringArray tokens;
|
||||
tokens.addTokens (adr, ".", String::empty);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
address[i] = (uint8) tokens[i].getIntValue();
|
||||
}
|
||||
|
||||
String IPAddress::toString() const
|
||||
{
|
||||
String s ((int) address[0]);
|
||||
|
||||
for (int i = 1; i < 4; ++i)
|
||||
s << '.' << (int) address[i];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
IPAddress IPAddress::any() noexcept { return IPAddress(); }
|
||||
IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); }
|
||||
IPAddress IPAddress::local() noexcept { return IPAddress (127, 0, 0, 1); }
|
||||
|
||||
bool IPAddress::operator== (const IPAddress& other) const noexcept
|
||||
{
|
||||
return address[0] == other.address[0]
|
||||
&& address[1] == other.address[1]
|
||||
&& address[2] == other.address[2]
|
||||
&& address[3] == other.address[3];
|
||||
}
|
||||
|
||||
bool IPAddress::operator!= (const IPAddress& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
#if ! BEAST_WINDOWS
|
||||
static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result)
|
||||
{
|
||||
in_addr_t addr = addr_in->sin_addr.s_addr;
|
||||
|
||||
if (addr != INADDR_NONE)
|
||||
result.addIfNotAlreadyThere (IPAddress (ntohl (addr)));
|
||||
}
|
||||
|
||||
static void findIPAddresses (int sock, Array<IPAddress>& result)
|
||||
{
|
||||
ifconf cfg;
|
||||
HeapBlock<char> buffer;
|
||||
size_t bufferSize = 1024;
|
||||
|
||||
do
|
||||
{
|
||||
bufferSize *= 2;
|
||||
buffer.calloc (bufferSize);
|
||||
|
||||
cfg.ifc_len = bufferSize;
|
||||
cfg.ifc_buf = buffer;
|
||||
|
||||
if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
|
||||
return;
|
||||
|
||||
} while (bufferSize < cfg.ifc_len + 2 * (IFNAMSIZ + sizeof (struct sockaddr_in6)));
|
||||
|
||||
#if BEAST_MAC || BEAST_IOS
|
||||
while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in)))
|
||||
{
|
||||
if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses
|
||||
addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result);
|
||||
|
||||
cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
|
||||
cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
|
||||
}
|
||||
#else
|
||||
for (size_t i = 0; i < cfg.ifc_len / sizeof (struct ifreq); ++i)
|
||||
{
|
||||
const ifreq& item = cfg.ifc_req[i];
|
||||
|
||||
if (item.ifr_addr.sa_family == AF_INET)
|
||||
addAddress ((const sockaddr_in*) &item.ifr_addr, result);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result)
|
||||
{
|
||||
const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
|
||||
|
||||
if (sock >= 0)
|
||||
{
|
||||
findIPAddresses (sock, result);
|
||||
::close (sock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,77 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_IPADDRESS_BEASTHEADER
|
||||
#define BEAST_IPADDRESS_BEASTHEADER
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An IPV4 address.
|
||||
*/
|
||||
class BEAST_API IPAddress
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Populates a list of all the IP addresses that this machine is using. */
|
||||
static void findAllAddresses (Array<IPAddress>& results);
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a null address (0.0.0.0). */
|
||||
IPAddress() noexcept;
|
||||
|
||||
/** Creates an address from 4 bytes. */
|
||||
explicit IPAddress (const uint8 bytes[4]) noexcept;
|
||||
|
||||
/** Creates an address from 4 bytes. */
|
||||
IPAddress (uint8 address1, uint8 address2, uint8 address3, uint8 address4) noexcept;
|
||||
|
||||
/** Creates an address from a packed 32-bit integer, where the MSB is
|
||||
the first number in the address, and the LSB is the last.
|
||||
*/
|
||||
explicit IPAddress (uint32 asNativeEndian32Bit) noexcept;
|
||||
|
||||
/** Parses a string IP address of the form "a.b.c.d". */
|
||||
explicit IPAddress (const String& address);
|
||||
|
||||
/** Returns a dot-separated string in the form "1.2.3.4" */
|
||||
String toString() const;
|
||||
|
||||
/** Returns an address meaning "any" (0.0.0.0) */
|
||||
static IPAddress any() noexcept;
|
||||
|
||||
/** Returns an address meaning "broadcast" (255.255.255.255) */
|
||||
static IPAddress broadcast() noexcept;
|
||||
|
||||
/** Returns an address meaning "localhost" (127.0.0.1) */
|
||||
static IPAddress local() noexcept;
|
||||
|
||||
bool operator== (const IPAddress& other) const noexcept;
|
||||
bool operator!= (const IPAddress& other) const noexcept;
|
||||
|
||||
/** The elements of the IP address. */
|
||||
uint8 address[4];
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_IPADDRESS_BEASTHEADER
|
||||
@@ -1,468 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
URL::URL()
|
||||
{
|
||||
}
|
||||
|
||||
URL::URL (const String& url_)
|
||||
: url (url_)
|
||||
{
|
||||
int i = url.indexOfChar ('?');
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
const int nextAmp = url.indexOfChar (i + 1, '&');
|
||||
const int equalsPos = url.indexOfChar (i + 1, '=');
|
||||
|
||||
if (equalsPos > i + 1)
|
||||
{
|
||||
if (nextAmp < 0)
|
||||
{
|
||||
addParameter (removeEscapeChars (url.substring (i + 1, equalsPos)),
|
||||
removeEscapeChars (url.substring (equalsPos + 1)));
|
||||
}
|
||||
else if (nextAmp > 0 && equalsPos < nextAmp)
|
||||
{
|
||||
addParameter (removeEscapeChars (url.substring (i + 1, equalsPos)),
|
||||
removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
|
||||
}
|
||||
}
|
||||
|
||||
i = nextAmp;
|
||||
}
|
||||
while (i >= 0);
|
||||
|
||||
url = url.upToFirstOccurrenceOf ("?", false, false);
|
||||
}
|
||||
}
|
||||
|
||||
URL::URL (const URL& other)
|
||||
: url (other.url),
|
||||
postData (other.postData),
|
||||
parameterNames (other.parameterNames),
|
||||
parameterValues (other.parameterValues),
|
||||
filesToUpload (other.filesToUpload),
|
||||
mimeTypes (other.mimeTypes)
|
||||
{
|
||||
}
|
||||
|
||||
URL& URL::operator= (const URL& other)
|
||||
{
|
||||
url = other.url;
|
||||
postData = other.postData;
|
||||
parameterNames = other.parameterNames;
|
||||
parameterValues = other.parameterValues;
|
||||
filesToUpload = other.filesToUpload;
|
||||
mimeTypes = other.mimeTypes;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool URL::operator== (const URL& other) const
|
||||
{
|
||||
return url == other.url
|
||||
&& postData == other.postData
|
||||
&& parameterNames == other.parameterNames
|
||||
&& parameterValues == other.parameterValues
|
||||
&& filesToUpload == other.filesToUpload
|
||||
&& mimeTypes == other.mimeTypes;
|
||||
}
|
||||
|
||||
bool URL::operator!= (const URL& other) const
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
URL::~URL()
|
||||
{
|
||||
}
|
||||
|
||||
namespace URLHelpers
|
||||
{
|
||||
static String getMangledParameters (const URL& url)
|
||||
{
|
||||
bassert (url.getParameterNames().size() == url.getParameterValues().size());
|
||||
String p;
|
||||
|
||||
for (int i = 0; i < url.getParameterNames().size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
p << '&';
|
||||
|
||||
p << URL::addEscapeChars (url.getParameterNames()[i], true)
|
||||
<< '='
|
||||
<< URL::addEscapeChars (url.getParameterValues()[i], true);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int findEndOfScheme (const String& url)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (CharacterFunctions::isLetterOrDigit (url[i])
|
||||
|| url[i] == '+' || url[i] == '-' || url[i] == '.')
|
||||
++i;
|
||||
|
||||
return url[i] == ':' ? i + 1 : 0;
|
||||
}
|
||||
|
||||
static int findStartOfNetLocation (const String& url)
|
||||
{
|
||||
int start = findEndOfScheme (url);
|
||||
while (url[start] == '/')
|
||||
++start;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static int findStartOfPath (const String& url)
|
||||
{
|
||||
return url.indexOfChar (findStartOfNetLocation (url), '/') + 1;
|
||||
}
|
||||
|
||||
static void createHeadersAndPostData (const URL& url, String& headers, MemoryBlock& postData)
|
||||
{
|
||||
MemoryOutputStream data (postData, false);
|
||||
|
||||
if (url.getFilesToUpload().size() > 0)
|
||||
{
|
||||
// need to upload some files, so do it as multi-part...
|
||||
const String boundary (String::toHexString (Random::getSystemRandom().nextInt64()));
|
||||
|
||||
headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
|
||||
|
||||
data << "--" << boundary;
|
||||
|
||||
for (int i = 0; i < url.getParameterNames().size(); ++i)
|
||||
{
|
||||
data << "\r\nContent-Disposition: form-data; name=\""
|
||||
<< url.getParameterNames() [i]
|
||||
<< "\"\r\n\r\n"
|
||||
<< url.getParameterValues() [i]
|
||||
<< "\r\n--"
|
||||
<< boundary;
|
||||
}
|
||||
|
||||
for (int i = 0; i < url.getFilesToUpload().size(); ++i)
|
||||
{
|
||||
const File file (url.getFilesToUpload().getAllValues() [i]);
|
||||
const String paramName (url.getFilesToUpload().getAllKeys() [i]);
|
||||
|
||||
data << "\r\nContent-Disposition: form-data; name=\"" << paramName
|
||||
<< "\"; filename=\"" << file.getFileName() << "\"\r\n";
|
||||
|
||||
const String mimeType (url.getMimeTypesOfUploadFiles()
|
||||
.getValue (paramName, String::empty));
|
||||
|
||||
if (mimeType.isNotEmpty())
|
||||
data << "Content-Type: " << mimeType << "\r\n";
|
||||
|
||||
data << "Content-Transfer-Encoding: binary\r\n\r\n"
|
||||
<< file << "\r\n--" << boundary;
|
||||
}
|
||||
|
||||
data << "--\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
data << getMangledParameters (url)
|
||||
<< url.getPostData();
|
||||
|
||||
// just a short text attachment, so use simple url encoding..
|
||||
headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: "
|
||||
<< (int) data.getDataSize() << "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void concatenatePaths (String& path, const String& suffix)
|
||||
{
|
||||
if (! path.endsWithChar ('/'))
|
||||
path << '/';
|
||||
|
||||
if (suffix.startsWithChar ('/'))
|
||||
path += suffix.substring (1);
|
||||
else
|
||||
path += suffix;
|
||||
}
|
||||
}
|
||||
|
||||
void URL::addParameter (const String& name, const String& value)
|
||||
{
|
||||
parameterNames.add (name);
|
||||
parameterValues.add (value);
|
||||
}
|
||||
|
||||
String URL::toString (const bool includeGetParameters) const
|
||||
{
|
||||
if (includeGetParameters && parameterNames.size() > 0)
|
||||
return url + "?" + URLHelpers::getMangledParameters (*this);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
bool URL::isWellFormed() const
|
||||
{
|
||||
//xxx TODO
|
||||
return url.isNotEmpty();
|
||||
}
|
||||
|
||||
String URL::getDomain() const
|
||||
{
|
||||
const int start = URLHelpers::findStartOfNetLocation (url);
|
||||
const int end1 = url.indexOfChar (start, '/');
|
||||
const int end2 = url.indexOfChar (start, ':');
|
||||
|
||||
const int end = (end1 < 0 && end2 < 0) ? std::numeric_limits<int>::max()
|
||||
: ((end1 < 0 || end2 < 0) ? bmax (end1, end2)
|
||||
: bmin (end1, end2));
|
||||
return url.substring (start, end);
|
||||
}
|
||||
|
||||
String URL::getSubPath() const
|
||||
{
|
||||
const int startOfPath = URLHelpers::findStartOfPath (url);
|
||||
|
||||
return startOfPath <= 0 ? String::empty
|
||||
: url.substring (startOfPath);
|
||||
}
|
||||
|
||||
String URL::getScheme() const
|
||||
{
|
||||
return url.substring (0, URLHelpers::findEndOfScheme (url) - 1);
|
||||
}
|
||||
|
||||
int URL::getPort() const
|
||||
{
|
||||
const int colonPos = url.indexOfChar (URLHelpers::findStartOfNetLocation (url), ':');
|
||||
|
||||
return colonPos > 0 ? url.substring (colonPos + 1).getIntValue() : 0;
|
||||
}
|
||||
|
||||
URL URL::withNewSubPath (const String& newPath) const
|
||||
{
|
||||
const int startOfPath = URLHelpers::findStartOfPath (url);
|
||||
|
||||
URL u (*this);
|
||||
|
||||
if (startOfPath > 0)
|
||||
u.url = url.substring (0, startOfPath);
|
||||
|
||||
URLHelpers::concatenatePaths (u.url, newPath);
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::getChildURL (const String& subPath) const
|
||||
{
|
||||
URL u (*this);
|
||||
URLHelpers::concatenatePaths (u.url, subPath);
|
||||
return u;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::isProbablyAWebsiteURL (const String& possibleURL)
|
||||
{
|
||||
const char* validProtocols[] = { "http:", "ftp:", "https:" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (validProtocols); ++i)
|
||||
if (possibleURL.startsWithIgnoreCase (validProtocols[i]))
|
||||
return true;
|
||||
|
||||
if (possibleURL.containsChar ('@')
|
||||
|| possibleURL.containsChar (' '))
|
||||
return false;
|
||||
|
||||
const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false)
|
||||
.fromLastOccurrenceOf (".", false, false));
|
||||
|
||||
return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3;
|
||||
}
|
||||
|
||||
bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
|
||||
{
|
||||
const int atSign = possibleEmailAddress.indexOfChar ('@');
|
||||
|
||||
return atSign > 0
|
||||
&& possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1)
|
||||
&& (! possibleEmailAddress.endsWithChar ('.'));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
InputStream* URL::createInputStream (const bool usePostCommand,
|
||||
OpenStreamProgressCallback* const progressCallback,
|
||||
void* const progressCallbackContext,
|
||||
const String& extraHeaders,
|
||||
const int timeOutMs,
|
||||
StringPairArray* const responseHeaders) const
|
||||
{
|
||||
String headers;
|
||||
MemoryBlock headersAndPostData;
|
||||
|
||||
if (usePostCommand)
|
||||
URLHelpers::createHeadersAndPostData (*this, headers, headersAndPostData);
|
||||
|
||||
headers += extraHeaders;
|
||||
|
||||
if (! headers.endsWithChar ('\n'))
|
||||
headers << "\r\n";
|
||||
|
||||
return createNativeStream (toString (! usePostCommand), usePostCommand, headersAndPostData,
|
||||
progressCallback, progressCallbackContext,
|
||||
headers, timeOutMs, responseHeaders);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::readEntireBinaryStream (MemoryBlock& destData,
|
||||
const bool usePostCommand) const
|
||||
{
|
||||
const ScopedPointer <InputStream> in (createInputStream (usePostCommand));
|
||||
|
||||
if (in != nullptr)
|
||||
{
|
||||
in->readIntoMemoryBlock (destData);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String URL::readEntireTextStream (const bool usePostCommand) const
|
||||
{
|
||||
const ScopedPointer <InputStream> in (createInputStream (usePostCommand));
|
||||
|
||||
if (in != nullptr)
|
||||
return in->readEntireStreamAsString();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const
|
||||
{
|
||||
return XmlDocument::parse (readEntireTextStream (usePostCommand));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
URL URL::withParameter (const String& parameterName,
|
||||
const String& parameterValue) const
|
||||
{
|
||||
URL u (*this);
|
||||
u.addParameter (parameterName, parameterValue);
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::withFileToUpload (const String& parameterName,
|
||||
const File& fileToUpload,
|
||||
const String& mimeType) const
|
||||
{
|
||||
bassert (mimeType.isNotEmpty()); // You need to supply a mime type!
|
||||
|
||||
URL u (*this);
|
||||
u.filesToUpload.set (parameterName, fileToUpload.getFullPathName());
|
||||
u.mimeTypes.set (parameterName, mimeType);
|
||||
return u;
|
||||
}
|
||||
|
||||
URL URL::withPOSTData (const String& postData_) const
|
||||
{
|
||||
URL u (*this);
|
||||
u.postData = postData_;
|
||||
return u;
|
||||
}
|
||||
|
||||
const StringPairArray& URL::getFilesToUpload() const
|
||||
{
|
||||
return filesToUpload;
|
||||
}
|
||||
|
||||
const StringPairArray& URL::getMimeTypesOfUploadFiles() const
|
||||
{
|
||||
return mimeTypes;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String URL::removeEscapeChars (const String& s)
|
||||
{
|
||||
String result (s.replaceCharacter ('+', ' '));
|
||||
|
||||
if (! result.containsChar ('%'))
|
||||
return result;
|
||||
|
||||
// We need to operate on the string as raw UTF8 chars, and then recombine them into unicode
|
||||
// after all the replacements have been made, so that multi-byte chars are handled.
|
||||
Array<char> utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8());
|
||||
|
||||
for (int i = 0; i < utf8.size(); ++i)
|
||||
{
|
||||
if (utf8.getUnchecked(i) == '%')
|
||||
{
|
||||
const int hexDigit1 = CharacterFunctions::getHexDigitValue ((beast_wchar) (uint8) utf8 [i + 1]);
|
||||
const int hexDigit2 = CharacterFunctions::getHexDigitValue ((beast_wchar) (uint8) utf8 [i + 2]);
|
||||
|
||||
if (hexDigit1 >= 0 && hexDigit2 >= 0)
|
||||
{
|
||||
utf8.set (i, (char) ((hexDigit1 << 4) + hexDigit2));
|
||||
utf8.removeRange (i + 1, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
|
||||
}
|
||||
|
||||
String URL::addEscapeChars (const String& s, const bool isParameter)
|
||||
{
|
||||
const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()"
|
||||
: ",$_-.*!'()");
|
||||
|
||||
Array<char> utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8());
|
||||
|
||||
for (int i = 0; i < utf8.size(); ++i)
|
||||
{
|
||||
const char c = utf8.getUnchecked(i);
|
||||
|
||||
if (! (CharacterFunctions::isLetterOrDigit (c)
|
||||
|| legalChars.indexOf ((beast_wchar) c) >= 0))
|
||||
{
|
||||
utf8.set (i, '%');
|
||||
utf8.insert (++i, "0123456789abcdef" [((uint8) c) >> 4]);
|
||||
utf8.insert (++i, "0123456789abcdef" [c & 15]);
|
||||
}
|
||||
}
|
||||
|
||||
return String::fromUTF8 (utf8.getRawDataPointer(), utf8.size());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::launchInDefaultBrowser() const
|
||||
{
|
||||
String u (toString (true));
|
||||
|
||||
if (u.containsChar ('@') && ! u.containsChar (':'))
|
||||
u = "mailto:" + u;
|
||||
|
||||
return Process::openDocument (u, String::empty);
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_URL_BEASTHEADER
|
||||
#define BEAST_URL_BEASTHEADER
|
||||
|
||||
#include "../text/beast_StringPairArray.h"
|
||||
#include "../files/beast_File.h"
|
||||
class InputStream;
|
||||
class XmlElement;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a URL and has a bunch of useful functions to manipulate it.
|
||||
|
||||
This class can be used to launch URLs in browsers, and also to create
|
||||
InputStreams that can read from remote http or ftp sources.
|
||||
*/
|
||||
class BEAST_API URL : LeakChecked <URL>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty URL. */
|
||||
URL();
|
||||
|
||||
/** Creates a URL from a string. */
|
||||
URL (const String& url);
|
||||
|
||||
/** Creates a copy of another URL. */
|
||||
URL (const URL& other);
|
||||
|
||||
/** Destructor. */
|
||||
~URL();
|
||||
|
||||
/** Copies this URL from another one. */
|
||||
URL& operator= (const URL& other);
|
||||
|
||||
/** Compares two URLs.
|
||||
All aspects of the URLs must be identical for them to match, including any parameters,
|
||||
upload files, etc.
|
||||
*/
|
||||
bool operator== (const URL&) const;
|
||||
bool operator!= (const URL&) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string version of the URL.
|
||||
|
||||
If includeGetParameters is true and any parameters have been set with the
|
||||
withParameter() method, then the string will have these appended on the
|
||||
end and url-encoded.
|
||||
*/
|
||||
String toString (bool includeGetParameters) const;
|
||||
|
||||
/** True if it seems to be valid. */
|
||||
bool isWellFormed() const;
|
||||
|
||||
/** Returns just the domain part of the URL.
|
||||
|
||||
E.g. for "http://www.xyz.com/foobar", this will return "www.xyz.com".
|
||||
*/
|
||||
String getDomain() const;
|
||||
|
||||
/** Returns the path part of the URL.
|
||||
|
||||
E.g. for "http://www.xyz.com/foo/bar?x=1", this will return "foo/bar".
|
||||
*/
|
||||
String getSubPath() const;
|
||||
|
||||
/** Returns the scheme of the URL.
|
||||
|
||||
E.g. for "http://www.xyz.com/foobar", this will return "http". (It won't
|
||||
include the colon).
|
||||
*/
|
||||
String getScheme() const;
|
||||
|
||||
/** Attempts to read a port number from the URL.
|
||||
@returns the port number, or 0 if none is explicitly specified.
|
||||
*/
|
||||
int getPort() const;
|
||||
|
||||
/** Returns a new version of this URL that uses a different sub-path.
|
||||
|
||||
E.g. if the URL is "http://www.xyz.com/foo?x=1" and you call this with
|
||||
"bar", it'll return "http://www.xyz.com/bar?x=1".
|
||||
*/
|
||||
URL withNewSubPath (const String& newPath) const;
|
||||
|
||||
/** Returns a new URL that refers to a sub-path relative to this one.
|
||||
|
||||
E.g. if the URL is "http://www.xyz.com/foo" and you call this with
|
||||
"bar", it'll return "http://www.xyz.com/foo/bar". Note that there's no way for
|
||||
this method to know whether the original URL is a file or directory, so it's
|
||||
up to you to make sure it's a directory. It also won't attempt to be smart about
|
||||
the content of the childPath string, so if this string is an absolute URL, it'll
|
||||
still just get bolted onto the end of the path.
|
||||
|
||||
@see File::getChildFile
|
||||
*/
|
||||
URL getChildURL (const String& subPath) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a copy of this URL, with a GET or POST parameter added to the end.
|
||||
|
||||
Any control characters in the value will be encoded.
|
||||
|
||||
e.g. calling "withParameter ("amount", "some fish") for the url "www.fish.com"
|
||||
would produce a new url whose toString(true) method would return
|
||||
"www.fish.com?amount=some+fish".
|
||||
|
||||
@see getParameterNames, getParameterValues
|
||||
*/
|
||||
URL withParameter (const String& parameterName,
|
||||
const String& parameterValue) const;
|
||||
|
||||
/** Returns a copy of this URl, with a file-upload type parameter added to it.
|
||||
|
||||
When performing a POST where one of your parameters is a binary file, this
|
||||
lets you specify the file.
|
||||
|
||||
Note that the filename is stored, but the file itself won't actually be read
|
||||
until this URL is later used to create a network input stream.
|
||||
*/
|
||||
URL withFileToUpload (const String& parameterName,
|
||||
const File& fileToUpload,
|
||||
const String& mimeType) const;
|
||||
|
||||
/** Returns an array of the names of all the URL's parameters.
|
||||
|
||||
E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would
|
||||
contain two items: "type" and "amount".
|
||||
|
||||
You can call getParameterValues() to get the corresponding value of each
|
||||
parameter. Note that the list can contain multiple parameters with the same name.
|
||||
|
||||
@see getParameterValues, withParameter
|
||||
*/
|
||||
const StringArray& getParameterNames() const noexcept { return parameterNames; }
|
||||
|
||||
/** Returns an array of the values of all the URL's parameters.
|
||||
|
||||
E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would
|
||||
contain two items: "haddock" and "some fish".
|
||||
|
||||
The values returned will have been cleaned up to remove any escape characters.
|
||||
|
||||
You can call getParameterNames() to get the corresponding name of each
|
||||
parameter. Note that the list can contain multiple parameters with the same name.
|
||||
|
||||
@see getParameterNames, withParameter
|
||||
*/
|
||||
const StringArray& getParameterValues() const noexcept { return parameterValues; }
|
||||
|
||||
/** Returns the set of files that should be uploaded as part of a POST operation.
|
||||
|
||||
This is the set of files that were added to the URL with the withFileToUpload()
|
||||
method.
|
||||
*/
|
||||
const StringPairArray& getFilesToUpload() const;
|
||||
|
||||
/** Returns the set of mime types associated with each of the upload files.
|
||||
*/
|
||||
const StringPairArray& getMimeTypesOfUploadFiles() const;
|
||||
|
||||
/** Returns a copy of this URL, with a block of data to send as the POST data.
|
||||
|
||||
If you're setting the POST data, be careful not to have any parameters set
|
||||
as well, otherwise it'll all get thrown in together, and might not have the
|
||||
desired effect.
|
||||
|
||||
If the URL already contains some POST data, this will replace it, rather
|
||||
than being appended to it.
|
||||
|
||||
This data will only be used if you specify a post operation when you call
|
||||
createInputStream().
|
||||
*/
|
||||
URL withPOSTData (const String& postData) const;
|
||||
|
||||
/** Returns the data that was set using withPOSTData(). */
|
||||
const String& getPostData() const noexcept { return postData; }
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to launch the system's default browser to open the URL.
|
||||
|
||||
Returns true if this seems to have worked.
|
||||
*/
|
||||
bool launchInDefaultBrowser() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Takes a guess as to whether a string might be a valid website address.
|
||||
|
||||
This isn't foolproof!
|
||||
*/
|
||||
static bool isProbablyAWebsiteURL (const String& possibleURL);
|
||||
|
||||
/** Takes a guess as to whether a string might be a valid email address.
|
||||
|
||||
This isn't foolproof!
|
||||
*/
|
||||
static bool isProbablyAnEmailAddress (const String& possibleEmailAddress);
|
||||
|
||||
//==============================================================================
|
||||
/** This callback function can be used by the createInputStream() method.
|
||||
|
||||
It allows your app to receive progress updates during a lengthy POST operation. If you
|
||||
want to continue the operation, this should return true, or false to abort.
|
||||
*/
|
||||
typedef bool (OpenStreamProgressCallback) (void* context, int bytesSent, int totalBytes);
|
||||
|
||||
/** Attempts to open a stream that can read from this URL.
|
||||
|
||||
@param usePostCommand if true, it will try to do use a http 'POST' to pass
|
||||
the paramters, otherwise it'll encode them into the
|
||||
URL and do a 'GET'.
|
||||
@param progressCallback if this is non-zero, it lets you supply a callback function
|
||||
to keep track of the operation's progress. This can be useful
|
||||
for lengthy POST operations, so that you can provide user feedback.
|
||||
@param progressCallbackContext if a callback is specified, this value will be passed to
|
||||
the function
|
||||
@param extraHeaders if not empty, this string is appended onto the headers that
|
||||
are used for the request. It must therefore be a valid set of HTML
|
||||
header directives, separated by newlines.
|
||||
@param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If
|
||||
a negative number, it will be infinite. Otherwise it specifies a
|
||||
time in milliseconds.
|
||||
@param responseHeaders if this is non-zero, all the (key, value) pairs received as headers
|
||||
in the response will be stored in this array
|
||||
@returns an input stream that the caller must delete, or a null pointer if there was an
|
||||
error trying to open it.
|
||||
*/
|
||||
InputStream* createInputStream (bool usePostCommand,
|
||||
OpenStreamProgressCallback* progressCallback = nullptr,
|
||||
void* progressCallbackContext = nullptr,
|
||||
const String& extraHeaders = String::empty,
|
||||
int connectionTimeOutMs = 0,
|
||||
StringPairArray* responseHeaders = nullptr) const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to download the entire contents of this URL into a binary data block.
|
||||
|
||||
If it succeeds, this will return true and append the data it read onto the end
|
||||
of the memory block.
|
||||
|
||||
@param destData the memory block to append the new data to
|
||||
@param usePostCommand whether to use a POST command to get the data (uses
|
||||
a GET command if this is false)
|
||||
@see readEntireTextStream, readEntireXmlStream
|
||||
*/
|
||||
bool readEntireBinaryStream (MemoryBlock& destData,
|
||||
bool usePostCommand = false) const;
|
||||
|
||||
/** Tries to download the entire contents of this URL as a string.
|
||||
|
||||
If it fails, this will return an empty string, otherwise it will return the
|
||||
contents of the downloaded file. If you need to distinguish between a read
|
||||
operation that fails and one that returns an empty string, you'll need to use
|
||||
a different method, such as readEntireBinaryStream().
|
||||
|
||||
@param usePostCommand whether to use a POST command to get the data (uses
|
||||
a GET command if this is false)
|
||||
@see readEntireBinaryStream, readEntireXmlStream
|
||||
*/
|
||||
String readEntireTextStream (bool usePostCommand = false) const;
|
||||
|
||||
/** Tries to download the entire contents of this URL and parse it as XML.
|
||||
|
||||
If it fails, or if the text that it reads can't be parsed as XML, this will
|
||||
return 0.
|
||||
|
||||
When it returns a valid XmlElement object, the caller is responsibile for deleting
|
||||
this object when no longer needed.
|
||||
|
||||
@param usePostCommand whether to use a POST command to get the data (uses
|
||||
a GET command if this is false)
|
||||
|
||||
@see readEntireBinaryStream, readEntireTextStream
|
||||
*/
|
||||
XmlElement* readEntireXmlStream (bool usePostCommand = false) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds escape sequences to a string to encode any characters that aren't
|
||||
legal in a URL.
|
||||
|
||||
E.g. any spaces will be replaced with "%20".
|
||||
|
||||
This is the opposite of removeEscapeChars().
|
||||
|
||||
If isParameter is true, it means that the string is going to be used
|
||||
as a parameter, so it also encodes '$' and ',' (which would otherwise
|
||||
be legal in a URL.
|
||||
|
||||
@see removeEscapeChars
|
||||
*/
|
||||
static String addEscapeChars (const String& stringToAddEscapeCharsTo,
|
||||
bool isParameter);
|
||||
|
||||
/** Replaces any escape character sequences in a string with their original
|
||||
character codes.
|
||||
|
||||
E.g. any instances of "%20" will be replaced by a space.
|
||||
|
||||
This is the opposite of addEscapeChars().
|
||||
|
||||
@see addEscapeChars
|
||||
*/
|
||||
static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String url, postData;
|
||||
StringArray parameterNames, parameterValues;
|
||||
StringPairArray filesToUpload, mimeTypes;
|
||||
|
||||
void addParameter (const String&, const String&);
|
||||
|
||||
static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
|
||||
OpenStreamProgressCallback* progressCallback,
|
||||
void* progressCallbackContext, const String& headers,
|
||||
const int timeOutMs, StringPairArray* responseHeaders);
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_URL_BEASTHEADER
|
||||
@@ -1,367 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Thread::Thread (const String& threadName_)
|
||||
: threadName (threadName_),
|
||||
threadHandle (nullptr),
|
||||
threadId (0),
|
||||
threadPriority (5),
|
||||
affinityMask (0),
|
||||
shouldExit (false)
|
||||
{
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
/* If your thread class's destructor has been called without first stopping the thread, that
|
||||
means that this partially destructed object is still performing some work - and that's
|
||||
probably a Bad Thing!
|
||||
|
||||
To avoid this type of nastiness, always make sure you call stopThread() before or during
|
||||
your subclass's destructor.
|
||||
*/
|
||||
fatal_assert (! isThreadRunning());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Use a ref-counted object to hold this shared data, so that it can outlive its static
|
||||
// shared pointer when threads are still running during static shutdown.
|
||||
struct CurrentThreadHolder : public SharedObject
|
||||
{
|
||||
CurrentThreadHolder() noexcept {}
|
||||
|
||||
typedef SharedObjectPtr <CurrentThreadHolder> Ptr;
|
||||
ThreadLocalValue<Thread*> value;
|
||||
};
|
||||
|
||||
static char currentThreadHolderLock [sizeof (SpinLock)]; // (statically initialised to zeros).
|
||||
|
||||
static SpinLock* castToSpinLockWithoutAliasingWarning (void* s)
|
||||
{
|
||||
return static_cast<SpinLock*> (s);
|
||||
}
|
||||
|
||||
static CurrentThreadHolder::Ptr getCurrentThreadHolder()
|
||||
{
|
||||
static CurrentThreadHolder::Ptr currentThreadHolder;
|
||||
SpinLock::ScopedLockType lock (*castToSpinLockWithoutAliasingWarning (currentThreadHolderLock));
|
||||
|
||||
if (currentThreadHolder == nullptr)
|
||||
currentThreadHolder = new CurrentThreadHolder();
|
||||
|
||||
return currentThreadHolder;
|
||||
}
|
||||
|
||||
void Thread::threadEntryPoint()
|
||||
{
|
||||
const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
|
||||
currentThreadHolder->value = this;
|
||||
|
||||
BEAST_TRY
|
||||
{
|
||||
if (threadName.isNotEmpty())
|
||||
setCurrentThreadName (threadName);
|
||||
|
||||
if (startSuspensionEvent.wait (10000))
|
||||
{
|
||||
bassert (getCurrentThreadId() == threadId);
|
||||
|
||||
if (affinityMask != 0)
|
||||
setCurrentThreadAffinityMask (affinityMask);
|
||||
|
||||
run();
|
||||
}
|
||||
}
|
||||
BEAST_CATCH_ALL_ASSERT
|
||||
|
||||
currentThreadHolder->value.releaseCurrentThreadStorage();
|
||||
closeThreadHandle();
|
||||
}
|
||||
|
||||
// used to wrap the incoming call from the platform-specific code
|
||||
void BEAST_API beast_threadEntryPoint (void* userData)
|
||||
{
|
||||
ProtectedCall (&Thread::threadEntryPoint, static_cast <Thread*> (userData));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::startThread()
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
shouldExit = false;
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
launchThread();
|
||||
setThreadPriority (threadHandle, threadPriority);
|
||||
startSuspensionEvent.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::startThread (const int priority)
|
||||
{
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (threadHandle == nullptr)
|
||||
{
|
||||
threadPriority = priority;
|
||||
startThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPriority (priority);
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::isThreadRunning() const
|
||||
{
|
||||
return threadHandle != nullptr;
|
||||
}
|
||||
|
||||
Thread* Thread::getCurrentThread()
|
||||
{
|
||||
return getCurrentThreadHolder()->value.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Thread::signalThreadShouldExit()
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const
|
||||
{
|
||||
// Doh! So how exactly do you expect this thread to wait for itself to stop??
|
||||
bassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0);
|
||||
|
||||
const uint32 timeoutEnd = Time::getMillisecondCounter() + (uint32) timeOutMilliseconds;
|
||||
|
||||
while (isThreadRunning())
|
||||
{
|
||||
if (timeOutMilliseconds >= 0 && Time::getMillisecondCounter() > timeoutEnd)
|
||||
return false;
|
||||
|
||||
sleep (2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Thread::stopThread (const int timeOutMilliseconds)
|
||||
{
|
||||
bool cleanExit = true;
|
||||
|
||||
// agh! You can't stop the thread that's calling this method! How on earth
|
||||
// would that work??
|
||||
bassert (getCurrentThreadId() != getThreadId());
|
||||
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
notify();
|
||||
|
||||
if (timeOutMilliseconds != 0)
|
||||
{
|
||||
cleanExit = waitForThreadToExit (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
if (isThreadRunning())
|
||||
{
|
||||
bassert (! cleanExit);
|
||||
|
||||
// very bad karma if this point is reached, as there are bound to be
|
||||
// locks and events left in silly states when a thread is killed by force..
|
||||
killThread();
|
||||
|
||||
threadHandle = nullptr;
|
||||
threadId = 0;
|
||||
|
||||
cleanExit = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanExit = true;
|
||||
}
|
||||
}
|
||||
|
||||
return cleanExit;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::setPriority (const int newPriority)
|
||||
{
|
||||
// NB: deadlock possible if you try to set the thread prio from the thread itself,
|
||||
// so using setCurrentThreadPriority instead in that case.
|
||||
if (getCurrentThreadId() == getThreadId())
|
||||
return setCurrentThreadPriority (newPriority);
|
||||
|
||||
const ScopedLock sl (startStopLock);
|
||||
|
||||
if (setThreadPriority (threadHandle, newPriority))
|
||||
{
|
||||
threadPriority = newPriority;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thread::setCurrentThreadPriority (const int newPriority)
|
||||
{
|
||||
return setThreadPriority (0, newPriority);
|
||||
}
|
||||
|
||||
void Thread::setAffinityMask (const uint32 newAffinityMask)
|
||||
{
|
||||
affinityMask = newAffinityMask;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool Thread::wait (const int timeOutMilliseconds) const
|
||||
{
|
||||
return defaultEvent.wait (timeOutMilliseconds);
|
||||
}
|
||||
|
||||
void Thread::notify() const
|
||||
{
|
||||
defaultEvent.signal();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SpinLock::enter() const noexcept
|
||||
{
|
||||
if (! tryEnter())
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
if (tryEnter())
|
||||
return;
|
||||
|
||||
while (! tryEnter())
|
||||
Thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
class AtomicTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
AtomicTests() : UnitTest ("Atomic", "beast") {}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTestCase ("Misc");
|
||||
|
||||
char a1[7];
|
||||
expect (numElementsInArray(a1) == 7);
|
||||
int a2[3];
|
||||
expect (numElementsInArray(a2) == 3);
|
||||
|
||||
expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211);
|
||||
expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211);
|
||||
expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211));
|
||||
|
||||
beginTestCase ("int");
|
||||
AtomicTester <int>::testInteger (*this);
|
||||
beginTestCase ("unsigned int");
|
||||
AtomicTester <unsigned int>::testInteger (*this);
|
||||
beginTestCase ("int32");
|
||||
AtomicTester <int32>::testInteger (*this);
|
||||
beginTestCase ("uint32");
|
||||
AtomicTester <uint32>::testInteger (*this);
|
||||
beginTestCase ("long");
|
||||
AtomicTester <long>::testInteger (*this);
|
||||
beginTestCase ("void*");
|
||||
AtomicTester <void*>::testInteger (*this);
|
||||
beginTestCase ("int*");
|
||||
AtomicTester <int*>::testInteger (*this);
|
||||
beginTestCase ("float");
|
||||
AtomicTester <float>::testFloat (*this);
|
||||
#if ! BEAST_64BIT_ATOMICS_UNAVAILABLE // 64-bit intrinsics aren't available on some old platforms
|
||||
beginTestCase ("int64");
|
||||
AtomicTester <int64>::testInteger (*this);
|
||||
beginTestCase ("uint64");
|
||||
AtomicTester <uint64>::testInteger (*this);
|
||||
beginTestCase ("double");
|
||||
AtomicTester <double>::testFloat (*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
class AtomicTester
|
||||
{
|
||||
public:
|
||||
AtomicTester() {}
|
||||
|
||||
static void testInteger (UnitTest& test)
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a.set ((Type) 10);
|
||||
test.expect (a.value == (Type) 10);
|
||||
test.expect (a.get() == (Type) 10);
|
||||
a += (Type) 15;
|
||||
test.expect (a.get() == (Type) 25);
|
||||
a.memoryBarrier();
|
||||
a -= (Type) 5;
|
||||
test.expect (a.get() == (Type) 20);
|
||||
test.expect (++a == (Type) 21);
|
||||
++a;
|
||||
test.expect (--a == (Type) 21);
|
||||
test.expect (a.get() == (Type) 21);
|
||||
a.memoryBarrier();
|
||||
|
||||
testFloat (test);
|
||||
}
|
||||
|
||||
static void testFloat (UnitTest& test)
|
||||
{
|
||||
Atomic<Type> a, b;
|
||||
a = (Type) 21;
|
||||
a.memoryBarrier();
|
||||
|
||||
/* These are some simple test cases to check the atomics - let me know
|
||||
if any of these assertions fail on your system!
|
||||
*/
|
||||
test.expect (a.get() == (Type) 21);
|
||||
test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21);
|
||||
test.expect (a.get() == (Type) 21);
|
||||
test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21);
|
||||
test.expect (a.get() == (Type) 101);
|
||||
test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200));
|
||||
test.expect (a.get() == (Type) 101);
|
||||
test.expect (a.compareAndSetBool ((Type) 200, a.get()));
|
||||
test.expect (a.get() == (Type) 200);
|
||||
|
||||
test.expect (a.exchange ((Type) 300) == (Type) 200);
|
||||
test.expect (a.get() == (Type) 300);
|
||||
|
||||
b = a;
|
||||
test.expect (b.get() == a.get());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static AtomicTests atomicTests;
|
||||
@@ -1,375 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ThreadPoolJob::ThreadPoolJob (const String& name)
|
||||
: jobName (name),
|
||||
pool (nullptr),
|
||||
shouldStop (false),
|
||||
isActive (false),
|
||||
shouldBeDeleted (false)
|
||||
{
|
||||
}
|
||||
|
||||
ThreadPoolJob::~ThreadPoolJob()
|
||||
{
|
||||
// you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob()
|
||||
// to remove it first!
|
||||
bassert (pool == nullptr || ! pool->contains (this));
|
||||
}
|
||||
|
||||
String ThreadPoolJob::getJobName() const
|
||||
{
|
||||
return jobName;
|
||||
}
|
||||
|
||||
void ThreadPoolJob::setJobName (const String& newName)
|
||||
{
|
||||
jobName = newName;
|
||||
}
|
||||
|
||||
void ThreadPoolJob::signalJobShouldExit()
|
||||
{
|
||||
shouldStop = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ThreadPool::ThreadPoolThread
|
||||
: public Thread
|
||||
, LeakChecked <ThreadPoolThread>
|
||||
{
|
||||
public:
|
||||
ThreadPoolThread (ThreadPool& pool_)
|
||||
: Thread ("Pool"),
|
||||
pool (pool_)
|
||||
{
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (! pool.runNextJob())
|
||||
wait (500);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadPool& pool;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool::ThreadPool (const int numThreads)
|
||||
{
|
||||
bassert (numThreads > 0); // not much point having a pool without any threads!
|
||||
|
||||
createThreads (numThreads);
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool()
|
||||
{
|
||||
createThreads (SystemStats::getNumCpus());
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
removeAllJobs (true, 5000);
|
||||
stopThreads();
|
||||
}
|
||||
|
||||
void ThreadPool::createThreads (int numThreads)
|
||||
{
|
||||
for (int i = bmax (1, numThreads); --i >= 0;)
|
||||
threads.add (new ThreadPoolThread (*this));
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->startThread();
|
||||
}
|
||||
|
||||
void ThreadPool::stopThreads()
|
||||
{
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->signalThreadShouldExit();
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->stopThread (500);
|
||||
}
|
||||
|
||||
void ThreadPool::addJob (ThreadPoolJob* const job, const bool deleteJobWhenFinished)
|
||||
{
|
||||
bassert (job != nullptr);
|
||||
bassert (job->pool == nullptr);
|
||||
|
||||
if (job->pool == nullptr)
|
||||
{
|
||||
job->pool = this;
|
||||
job->shouldStop = false;
|
||||
job->isActive = false;
|
||||
job->shouldBeDeleted = deleteJobWhenFinished;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
jobs.add (job);
|
||||
}
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
threads.getUnchecked(i)->notify();
|
||||
}
|
||||
}
|
||||
|
||||
int ThreadPool::getNumJobs() const
|
||||
{
|
||||
return jobs.size();
|
||||
}
|
||||
|
||||
ThreadPoolJob* ThreadPool::getJob (const int index) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs [index];
|
||||
}
|
||||
|
||||
bool ThreadPool::contains (const ThreadPoolJob* const job) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs.contains (const_cast <ThreadPoolJob*> (job));
|
||||
}
|
||||
|
||||
bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
return jobs.contains (const_cast <ThreadPoolJob*> (job)) && job->isActive;
|
||||
}
|
||||
|
||||
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job,
|
||||
const int timeOutMs) const
|
||||
{
|
||||
if (job != nullptr)
|
||||
{
|
||||
const uint32 start = Time::getMillisecondCounter();
|
||||
|
||||
while (contains (job))
|
||||
{
|
||||
if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs)
|
||||
return false;
|
||||
|
||||
jobFinishedSignal.wait (2);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThreadPool::removeJob (ThreadPoolJob* const job,
|
||||
const bool interruptIfRunning,
|
||||
const int timeOutMs)
|
||||
{
|
||||
bool dontWait = true;
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
if (job != nullptr)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (jobs.contains (job))
|
||||
{
|
||||
if (job->isActive)
|
||||
{
|
||||
if (interruptIfRunning)
|
||||
job->signalJobShouldExit();
|
||||
|
||||
dontWait = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs.removeFirstMatchingValue (job);
|
||||
addToDeleteList (deletionList, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dontWait || waitForJobToFinish (job, timeOutMs);
|
||||
}
|
||||
|
||||
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs,
|
||||
ThreadPool::JobSelector* selectedJobsToRemove)
|
||||
{
|
||||
Array <ThreadPoolJob*> jobsToWaitFor;
|
||||
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = jobs.size(); --i >= 0;)
|
||||
{
|
||||
ThreadPoolJob* const job = jobs.getUnchecked(i);
|
||||
|
||||
if (selectedJobsToRemove == nullptr || selectedJobsToRemove->isJobSuitable (job))
|
||||
{
|
||||
if (job->isActive)
|
||||
{
|
||||
jobsToWaitFor.add (job);
|
||||
|
||||
if (interruptRunningJobs)
|
||||
job->signalJobShouldExit();
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs.remove (i);
|
||||
addToDeleteList (deletionList, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint32 start = Time::getMillisecondCounter();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (int i = jobsToWaitFor.size(); --i >= 0;)
|
||||
{
|
||||
ThreadPoolJob* const job = jobsToWaitFor.getUnchecked (i);
|
||||
|
||||
if (! isJobRunning (job))
|
||||
jobsToWaitFor.remove (i);
|
||||
}
|
||||
|
||||
if (jobsToWaitFor.size() == 0)
|
||||
break;
|
||||
|
||||
if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs)
|
||||
return false;
|
||||
|
||||
jobFinishedSignal.wait (20);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const
|
||||
{
|
||||
StringArray s;
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < jobs.size(); ++i)
|
||||
{
|
||||
const ThreadPoolJob* const job = jobs.getUnchecked(i);
|
||||
if (job->isActive || ! onlyReturnActiveJobs)
|
||||
s.add (job->getJobName());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ThreadPool::setThreadPriorities (const int newPriority)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
for (int i = threads.size(); --i >= 0;)
|
||||
if (! threads.getUnchecked(i)->setPriority (newPriority))
|
||||
ok = false;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
ThreadPoolJob* ThreadPool::pickNextJobToRun()
|
||||
{
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < jobs.size(); ++i)
|
||||
{
|
||||
ThreadPoolJob* job = jobs[i];
|
||||
|
||||
if (job != nullptr && ! job->isActive)
|
||||
{
|
||||
if (job->shouldStop)
|
||||
{
|
||||
jobs.remove (i);
|
||||
addToDeleteList (deletionList, job);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
job->isActive = true;
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ThreadPool::runNextJob()
|
||||
{
|
||||
ThreadPoolJob* const job = pickNextJobToRun();
|
||||
|
||||
if (job == nullptr)
|
||||
return false;
|
||||
|
||||
ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished;
|
||||
|
||||
BEAST_TRY
|
||||
{
|
||||
result = job->runJob();
|
||||
}
|
||||
BEAST_CATCH_ALL_ASSERT
|
||||
|
||||
OwnedArray<ThreadPoolJob> deletionList;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (jobs.contains (job))
|
||||
{
|
||||
job->isActive = false;
|
||||
|
||||
if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
|
||||
{
|
||||
jobs.removeFirstMatchingValue (job);
|
||||
addToDeleteList (deletionList, job);
|
||||
|
||||
jobFinishedSignal.signal();
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the job to the end of the queue if it wants another go
|
||||
jobs.move (jobs.indexOf (job), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThreadPool::addToDeleteList (OwnedArray<ThreadPoolJob>& deletionList, ThreadPoolJob* const job) const
|
||||
{
|
||||
job->shouldStop = true;
|
||||
job->pool = nullptr;
|
||||
|
||||
if (job->shouldBeDeleted)
|
||||
deletionList.add (job);
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_THREADPOOL_BEASTHEADER
|
||||
#define BEAST_THREADPOOL_BEASTHEADER
|
||||
|
||||
#include "beast_Thread.h"
|
||||
#include "../text/beast_StringArray.h"
|
||||
#include "../containers/beast_Array.h"
|
||||
#include "../containers/beast_OwnedArray.h"
|
||||
class ThreadPool;
|
||||
class ThreadPoolThread;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A task that is executed by a ThreadPool object.
|
||||
|
||||
A ThreadPool keeps a list of ThreadPoolJob objects which are executed by
|
||||
its threads.
|
||||
|
||||
The runJob() method needs to be implemented to do the task, and if the code that
|
||||
does the work takes a significant time to run, it must keep checking the shouldExit()
|
||||
method to see if something is trying to interrupt the job. If shouldExit() returns
|
||||
true, the runJob() method must return immediately.
|
||||
|
||||
@see ThreadPool, Thread
|
||||
*/
|
||||
class BEAST_API ThreadPoolJob : LeakChecked <ThreadPoolJob>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a thread pool job object.
|
||||
After creating your job, add it to a thread pool with ThreadPool::addJob().
|
||||
*/
|
||||
explicit ThreadPoolJob (const String& name);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ThreadPoolJob();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the name of this job.
|
||||
@see setJobName
|
||||
*/
|
||||
String getJobName() const;
|
||||
|
||||
/** Changes the job's name.
|
||||
@see getJobName
|
||||
*/
|
||||
void setJobName (const String& newName);
|
||||
|
||||
//==============================================================================
|
||||
/** These are the values that can be returned by the runJob() method.
|
||||
*/
|
||||
enum JobStatus
|
||||
{
|
||||
jobHasFinished = 0, /**< indicates that the job has finished and can be
|
||||
removed from the pool. */
|
||||
|
||||
jobNeedsRunningAgain /**< indicates that the job would like to be called
|
||||
again when a thread is free. */
|
||||
};
|
||||
|
||||
/** Peforms the actual work that this job needs to do.
|
||||
|
||||
Your subclass must implement this method, in which is does its work.
|
||||
|
||||
If the code in this method takes a significant time to run, it must repeatedly check
|
||||
the shouldExit() method to see if something is trying to interrupt the job.
|
||||
If shouldExit() ever returns true, the runJob() method must return immediately.
|
||||
|
||||
If this method returns jobHasFinished, then the job will be removed from the pool
|
||||
immediately. If it returns jobNeedsRunningAgain, then the job will be left in the
|
||||
pool and will get a chance to run again as soon as a thread is free.
|
||||
|
||||
@see shouldExit()
|
||||
*/
|
||||
virtual JobStatus runJob() = 0;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this job is currently running its runJob() method. */
|
||||
bool isRunning() const noexcept { return isActive; }
|
||||
|
||||
/** Returns true if something is trying to interrupt this job and make it stop.
|
||||
|
||||
Your runJob() method must call this whenever it gets a chance, and if it ever
|
||||
returns true, the runJob() method must return immediately.
|
||||
|
||||
@see signalJobShouldExit()
|
||||
*/
|
||||
bool shouldExit() const noexcept { return shouldStop; }
|
||||
|
||||
/** Calling this will cause the shouldExit() method to return true, and the job
|
||||
should (if it's been implemented correctly) stop as soon as possible.
|
||||
|
||||
@see shouldExit()
|
||||
*/
|
||||
void signalJobShouldExit();
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
friend class ThreadPool;
|
||||
friend class ThreadPoolThread;
|
||||
String jobName;
|
||||
ThreadPool* pool;
|
||||
bool shouldStop, isActive, shouldBeDeleted;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A set of threads that will run a list of jobs.
|
||||
|
||||
When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method
|
||||
will be called by the next pooled thread that becomes free.
|
||||
|
||||
@see ThreadPoolJob, Thread
|
||||
*/
|
||||
class BEAST_API ThreadPool : LeakChecked <ThreadPool>, public Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a thread pool.
|
||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||
@param numberOfThreads the number of threads to run. These will be started
|
||||
immediately, and will run until the pool is deleted.
|
||||
*/
|
||||
ThreadPool (int numberOfThreads);
|
||||
|
||||
/** Creates a thread pool with one thread per CPU core.
|
||||
Once you've created a pool, you can give it some jobs by calling addJob().
|
||||
If you want to specify the number of threads, use the other constructor; this
|
||||
one creates a pool which has one thread for each CPU core.
|
||||
@see SystemStats::getNumCpus()
|
||||
*/
|
||||
ThreadPool();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
This will attempt to remove all the jobs before deleting, but if you want to
|
||||
specify a timeout, you should call removeAllJobs() explicitly before deleting
|
||||
the pool.
|
||||
*/
|
||||
~ThreadPool();
|
||||
|
||||
//==============================================================================
|
||||
/** A callback class used when you need to select which ThreadPoolJob objects are suitable
|
||||
for some kind of operation.
|
||||
@see ThreadPool::removeAllJobs
|
||||
*/
|
||||
class BEAST_API JobSelector
|
||||
{
|
||||
public:
|
||||
virtual ~JobSelector() {}
|
||||
|
||||
/** Should return true if the specified thread matches your criteria for whatever
|
||||
operation that this object is being used for.
|
||||
|
||||
Any implementation of this method must be extremely fast and thread-safe!
|
||||
*/
|
||||
virtual bool isJobSuitable (ThreadPoolJob* job) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a job to the queue.
|
||||
|
||||
Once a job has been added, then the next time a thread is free, it will run
|
||||
the job's ThreadPoolJob::runJob() method. Depending on the return value of the
|
||||
runJob() method, the pool will either remove the job from the pool or add it to
|
||||
the back of the queue to be run again.
|
||||
|
||||
If deleteJobWhenFinished is true, then the job object will be owned and deleted by
|
||||
the pool when not needed - if you do this, make sure that your object's destructor
|
||||
is thread-safe.
|
||||
|
||||
If deleteJobWhenFinished is false, the pointer will be used but not deleted, and
|
||||
the caller is responsible for making sure the object is not deleted before it has
|
||||
been removed from the pool.
|
||||
*/
|
||||
void addJob (ThreadPoolJob* job,
|
||||
bool deleteJobWhenFinished);
|
||||
|
||||
/** Tries to remove a job from the pool.
|
||||
|
||||
If the job isn't yet running, this will simply remove it. If it is running, it
|
||||
will wait for it to finish.
|
||||
|
||||
If the timeout period expires before the job finishes running, then the job will be
|
||||
left in the pool and this will return false. It returns true if the job is sucessfully
|
||||
stopped and removed.
|
||||
|
||||
@param job the job to remove
|
||||
@param interruptIfRunning if true, then if the job is currently busy, its
|
||||
ThreadPoolJob::signalJobShouldExit() method will be called to try
|
||||
to interrupt it. If false, then if the job will be allowed to run
|
||||
until it stops normally (or the timeout expires)
|
||||
@param timeOutMilliseconds the length of time this method should wait for the job to finish
|
||||
before giving up and returning false
|
||||
*/
|
||||
bool removeJob (ThreadPoolJob* job,
|
||||
bool interruptIfRunning,
|
||||
int timeOutMilliseconds);
|
||||
|
||||
/** Tries to remove all jobs from the pool.
|
||||
|
||||
@param interruptRunningJobs if true, then all running jobs will have their ThreadPoolJob::signalJobShouldExit()
|
||||
methods called to try to interrupt them
|
||||
@param timeOutMilliseconds the length of time this method should wait for all the jobs to finish
|
||||
before giving up and returning false
|
||||
@param selectedJobsToRemove if this is non-zero, the JobSelector object is asked to decide which
|
||||
jobs should be removed. If it is zero, all jobs are removed
|
||||
@returns true if all jobs are successfully stopped and removed; false if the timeout period
|
||||
expires while waiting for one or more jobs to stop
|
||||
*/
|
||||
bool removeAllJobs (bool interruptRunningJobs,
|
||||
int timeOutMilliseconds,
|
||||
JobSelector* selectedJobsToRemove = nullptr);
|
||||
|
||||
/** Returns the number of jobs currently running or queued.
|
||||
*/
|
||||
int getNumJobs() const;
|
||||
|
||||
/** Returns one of the jobs in the queue.
|
||||
|
||||
Note that this can be a very volatile list as jobs might be continuously getting shifted
|
||||
around in the list, and this method may return 0 if the index is currently out-of-range.
|
||||
*/
|
||||
ThreadPoolJob* getJob (int index) const;
|
||||
|
||||
/** Returns true if the given job is currently queued or running.
|
||||
|
||||
@see isJobRunning()
|
||||
*/
|
||||
bool contains (const ThreadPoolJob* job) const;
|
||||
|
||||
/** Returns true if the given job is currently being run by a thread.
|
||||
*/
|
||||
bool isJobRunning (const ThreadPoolJob* job) const;
|
||||
|
||||
/** Waits until a job has finished running and has been removed from the pool.
|
||||
|
||||
This will wait until the job is no longer in the pool - i.e. until its
|
||||
runJob() method returns ThreadPoolJob::jobHasFinished.
|
||||
|
||||
If the timeout period expires before the job finishes, this will return false;
|
||||
it returns true if the job has finished successfully.
|
||||
*/
|
||||
bool waitForJobToFinish (const ThreadPoolJob* job,
|
||||
int timeOutMilliseconds) const;
|
||||
|
||||
/** Returns a list of the names of all the jobs currently running or queued.
|
||||
If onlyReturnActiveJobs is true, only the ones currently running are returned.
|
||||
*/
|
||||
StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
|
||||
|
||||
/** Changes the priority of all the threads.
|
||||
|
||||
This will call Thread::setPriority() for each thread in the pool.
|
||||
May return false if for some reason the priority can't be changed.
|
||||
*/
|
||||
bool setThreadPriorities (int newPriority);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <ThreadPoolJob*> jobs;
|
||||
|
||||
class ThreadPoolThread;
|
||||
friend class ThreadPoolThread;
|
||||
friend class OwnedArray <ThreadPoolThread>;
|
||||
OwnedArray <ThreadPoolThread> threads;
|
||||
|
||||
CriticalSection lock;
|
||||
WaitableEvent jobFinishedSignal;
|
||||
|
||||
bool runNextJob();
|
||||
ThreadPoolJob* pickNextJobToRun();
|
||||
void addToDeleteList (OwnedArray<ThreadPoolJob>&, ThreadPoolJob*) const;
|
||||
void createThreads (int numThreads);
|
||||
void stopThreads();
|
||||
|
||||
// Note that this method has changed, and no longer has a parameter to indicate
|
||||
// whether the jobs should be deleted - see the new method for details.
|
||||
void removeAllJobs (bool, int, bool);
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_THREADPOOL_BEASTHEADER
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user